Nginx 是高性能 Web Server 和 Reverse Proxy(反向代理服务器)。
主要用途:
- 提供静态资源(HTML、JS、CSS、图片)。
- 反向代理 Django、FastAPI 等后端。
- HTTPS / TLS 终止。
- HTTP 压缩与缓存。
- 负载均衡和限流。
- WebSocket、SSE 等长连接或流式响应代理。
对于 React + Django + Docker 项目:
Browser
↓
Cloudflare(可选)
↓
Nginx
├── / → React SPA
├── /assets/ → JS / CSS
├── /api/ → Django DRF
├── /admin/ → Django Admin
└── /static/ → Django Static Files
1. nginx.conf 配置结构
Nginx 使用不同的配置上下文(Context)组织配置。
events {
worker_connections 1024;
}
http {
server {
listen 80;
server_name example.com;
location / {
# ...
}
location /api/ {
# ...
}
}
}
主要层级:
nginx.conf
├── Main Context
├── events
└── http
├── upstream
├── server
│ ├── location /
│ └── location /api/
└── server
| Context | 作用 |
|---|---|
| main | 全局设置,例如 worker_processes |
| events | 连接事件处理 |
| http | HTTP 服务的公共配置 |
| upstream | 定义后端服务器组 |
| server | 虚拟主机,处理指定端口与域名 |
| location | 按 URI 匹配并处理请求 |
worker_processes 与 worker_connections
worker_processes auto;
events {
worker_connections 1024;
}
worker_processes:Nginx Worker 进程数,auto通常根据可用 CPU 资源确定。worker_connections:每个 Worker 允许的最大连接数,而不是最大请求数。
理论连接容量大致受到两者乘积限制,但反向代理时,一个客户端请求可能同时占用客户端连接和上游连接,因此不能直接把乘积当作最大并发用户数。
Docker 中的配置文件
官方 Nginx 镜像通常使用:
/etc/nginx/nginx.conf
/etc/nginx/conf.d/default.conf
一般来说:
nginx.conf:包含events、http等顶层结构。conf.d/default.conf:通常只定义serverBlock,由主配置通过include引入。
因此,挂载到 conf.d/default.conf 的配置文件一般不需要再写 http {} 和 events {}。
2. Server Block 与 HTTPS
HTTP 重定向 HTTPS
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
例如:
http://example.com/blog?id=1
↓ 301
https://example.com/blog?id=1
listen 80:监听 HTTP 80 端口。server_name:匹配请求域名。return 301:永久重定向。$host:Nginx 解析得到的请求主机名。$request_uri:原始请求 URI,包含 Query String。
HTTPS Server
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/ssl/fullchain.pem;
ssl_certificate_key /etc/ssl/privkey.pem;
client_max_body_size 10M;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log warn;
}
Nginx 负责 TLS 握手、证书以及加密 HTTP 连接。
当使用 Cloudflare 时,可以采用:
Browser
↓ HTTPS
Cloudflare
↓ HTTPS
Nginx
↓ HTTP(内部网络)
Django
通常应使用 Cloudflare Full (strict),并为源站配置有效证书,确保 Cloudflare 到 Nginx 的连接也经过验证。
3. Location:URI 匹配
location 用于根据请求 URI 选择处理方式。
location / {
# ...
}
location /api/ {
# ...
}
常见匹配方式
| 语法 | 含义 |
|---|---|
location = /api | 精确匹配 |
location /api/ | 前缀匹配 |
location ^~ /assets/ | 前缀匹配且跳过正则匹配检查 |
location ~ \.css$ | 区分大小写的正则匹配 |
location ~* \.(png|jpg)$ | 不区分大小写的正则匹配 |
匹配优先级
基本规则:
- 精确匹配
=优先。 - 找到最长前缀匹配。
- 如果最长前缀使用
^~,直接选择它。 - 否则按照配置顺序检查正则 Location,第一个匹配的正则生效。
- 没有正则匹配时,使用之前找到的最长前缀。
注意:URI 匹配不包含 Query String。
4. Reverse Proxy:反向代理
假设 Django 在 Docker 中监听:
backend:8000
Nginx:
location /api/ {
proxy_pass http://backend:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
请求:
GET https://example.com/api/v1/users/me/
↓
Nginx
↓
GET http://backend:8000/api/v1/users/me/
其中 backend 可以是 Docker Compose 的服务名称。
proxy_pass 的末尾斜杠
这是容易出错的地方。
location /api/ {
proxy_pass http://backend:8000;
}
保留路径前缀:
/api/v1/users/ → /api/v1/users/
但是:
location /api/ {
proxy_pass http://backend:8000/;
}
会将匹配的 /api/ 前缀替换成 /:
/api/v1/users/ → /v1/users/
原因是第二个 proxy_pass 包含 URI /,Nginx 会执行对应的路径替换。
对于 Django 已经定义 /api/v1/ 前缀的项目,通常使用不带末尾斜杠的 proxy_pass http://backend:8000;。
5. proxy_set_header:传递请求信息
| Header | 作用 |
|---|---|
| Host | 原始请求域名 |
| X-Real-IP | 告诉后端客户端 IP |
| X-Forwarded-For | 记录代理链中的 IP |
| X-Forwarded-Proto | 告诉后端原始请求协议 |
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
常见变量:
| 变量 | 含义 |
|---|---|
$host | 解析后的请求主机名 |
$remote_addr | 当前 Nginx 识别的客户端地址 |
$proxy_add_x_forwarded_for | 原有 X-Forwarded-For 追加当前客户端地址 |
$scheme | 当前连接使用的 HTTP 或 HTTPS |
$request_uri | 原始 URI,包含 Query String |
$uri | 当前规范化 URI,不包含 Query String |
$http_upgrade | 请求中的 Upgrade Header |
Cloudflare 与真实客户端 IP
如果使用 Cloudflare 代理:
Browser IP
↓
Cloudflare IP
↓
Nginx
Nginx 默认看到的 $remote_addr 可能是 Cloudflare 节点 IP。
可以通过 Real IP 模块恢复访客 IP:
# 仅示意,替换为 Cloudflare 官方公布的完整 IP 段
set_real_ip_from <trusted_cloudflare_cidr>;
real_ip_header CF-Connecting-IP;
必须只信任真实 Cloudflare 代理地址,并确保源站不会接受可伪造的非可信代理请求。
恢复后,$remote_addr 才会代表经过可信代理信息识别的客户端地址。
Cloudflare IP 列表需要定期维护。参考 Cloudflare 官方文档。
6. Static Files 与 SPA 路由
Vite 构建结果:
dist/
├── index.html
└── assets/
├── index-Ab12.js
└── index-Cd34.css
Nginx:
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
处理流程:
GET /profile
↓
检查 /usr/share/nginx/html/profile
↓
不存在
↓
返回 index.html
↓
React Router 处理 /profile
root 与 alias
root 把请求 URI 拼接到静态文件根目录:
location /assets/ {
root /usr/share/nginx/html;
}
请求:
/assets/app.js
→ /usr/share/nginx/html/assets/app.js
alias 用指定文件系统路径替换匹配的 Location 前缀:
location /media/ {
alias /data/media/;
}
请求:
/media/avatar/a.jpg
→ /data/media/avatar/a.jpg
对于目录型 Location,alias 路径通常需要保持尾部 / 与前缀对应。
7. HTTP 缓存
对于 Vite 生成的带 Hash 静态文件:
location /assets/ {
try_files $uri =404;
expires 1y;
add_header Cache-Control "public, immutable";
}
这样:
index-A1b2.js
可以缓存一年。
构建更新后生成:
index-C3d4.js
浏览器会将其视为新资源。
index.html
location = /index.html {
add_header Cache-Control "no-cache";
}
no-cache 表示使用缓存之前需要验证,而不是完全禁止缓存。
注意:SPA 的 /profile 等路径可能通过 /index.html 内部重定向返回 HTML,因此也需要确认最终 HTML 响应具有适当的缓存 Header。
不要将 SPA 的回退 HTML 长期缓存,也不要让不存在的 JS 文件回退成 HTML。
8. gzip / Brotli 压缩
Nginx 可以压缩文本类 HTTP 响应,减少网络传输体积。
gzip
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_comp_level 6;
gzip_types
text/plain
text/css
application/javascript
application/json
image/svg+xml;
Brotli
需要安装兼容的 Brotli Nginx 模块:
brotli on;
brotli_comp_level 5;
brotli_types
text/plain
text/css
application/javascript
application/json
image/svg+xml;
两种算法可以同时配置。
Browser
↓ Accept-Encoding: br, gzip
Nginx
↓ Content-Encoding: br
Compressed Response
通常优先 Brotli,gzip 作为回退。
注意:官方 nginx:alpine 镜像通常不能直接启用 Brotli 指令,必须先确保对应模块存在并正确加载。
9. 请求大小与代理超时
client_max_body_size
client_max_body_size 10M;
限制客户端请求体大小。
超过限制通常返回:
413 Content Too Large
适合限制头像、图片和其他上传内容。
代理超时
proxy_connect_timeout 5s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
| 指令 | 作用 |
|---|---|
| proxy_connect_timeout | 建立上游连接的超时时间 |
| proxy_send_timeout | 连续两次向上游写入操作之间允许的超时 |
| proxy_read_timeout | 连续两次从上游读取操作之间允许的超时 |
proxy_read_timeout 不是整个 HTTP 请求的总执行时间限制。
如果后端持续发送数据,即使总响应时间超过 60 秒,也未必触发该超时。
10. Buffering:响应缓冲
Nginx 默认开启代理响应缓冲:
proxy_buffering on;
一般 API 响应可以保持默认。
工作方式:
Django
↓
Nginx Buffers
↓
Browser
Nginx 会尽可能读取上游响应并缓冲,有利于降低慢客户端对后端的占用。
但对于 SSE 等需要立即传输数据的接口,可以关闭:
proxy_buffering off;
此时响应会更及时地向客户端传递。
proxy_request_buffering 则是另一项配置,它控制请求体是否先在 Nginx 缓冲,不能与响应缓冲混淆。
11. WebSocket 代理
WebSocket 在 HTTP/1.1 连接升级时需要特殊 Header。
location /ws/ {
proxy_pass http://backend:8000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 3600s;
}
基本过程:
Browser
↓ HTTP Upgrade
Nginx
↓
ASGI / WebSocket Server
↓
Persistent Connection
对于混合普通 HTTP 与 WebSocket 的配置,也可以通过 map 动态设置 Connection Header。
注意:Django 本身并不直接提供完整的 WebSocket 应用路由,一般需要 Django Channels 或其他 ASGI WebSocket 处理方案。
12. SSE / Streaming
SSE(Server-Sent Events)用于服务器持续向客户端发送事件,通信方向主要是:
Server → Browser
典型场景:
- AI Token 流式输出。
- 任务进度更新。
- 实时通知。
- 服务器事件推送。
Nginx 配置
location /api/v1/stream/ {
proxy_pass http://backend:8000;
proxy_http_version 1.1;
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 300s;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
}
关键配置:
proxy_buffering off:避免 Nginx 缓冲响应,延迟事件输出。proxy_cache off:不使用 Nginx 代理缓存。proxy_read_timeout 300s:允许两次上游读取之间较长的等待时间。
后端通常返回:
Content-Type: text/event-stream
Cache-Control: no-cache
响应内容:
data: {"message":"Hello"}
data: {"message":"World"}
每个事件以空行分隔。
对于长时间没有新事件的连接,服务器可以定期发送心跳,避免连接因空闲而超时。
此外,也可以让后端返回:
X-Accel-Buffering: no
针对特定响应禁用 Nginx 缓冲,但必须确认没有被 proxy_ignore_headers 禁用。
13. 负载均衡
当存在多个后端实例时:
upstream django_backend {
server backend1:8000;
server backend2:8000;
server backend3:8000;
}
server {
location /api/ {
proxy_pass http://django_backend;
}
}
架构:
┌── Django 1
Browser │
↓ ├── Django 2
Nginx ───────┤
└── Django 3
默认常用轮询方式分配请求。
其他策略包括:
least_conn:优先选择活动连接更少的后端。ip_hash:根据客户端 IP 进行相对固定的分配。
对于部署在 Docker Compose 单台 VPS 上的小型项目,不一定需要多实例负载均衡。
14. 限流(Rate Limiting)
Nginx 可以按照 IP 等条件限制请求频率。
在 http 上下文定义:
limit_req_zone
$binary_remote_addr
zone=api:10m
rate=10r/s;
在 Location 使用:
location /api/ {
limit_req zone=api burst=20;
limit_req_status 429;
proxy_pass http://backend:8000;
}
关键参数:
| 参数 | 作用 |
|---|---|
$binary_remote_addr | 客户端 IP 的二进制表示 |
zone=api:10m | 限流状态共享内存区域 |
rate=10r/s | 平均每秒 10 个请求 |
burst=20 | 允许一定程度的请求突发 |
limit_req_status 429 | 触发限流时返回 429 |
不设置 nodelay 时,Burst 内的部分超额请求可能会被延迟处理,而不是立即通过。
使用 Cloudflare 时,应先正确恢复真实客户端 IP,否则可能按照 Cloudflare 节点 IP 限流,导致多个真实用户共享同一个限流身份。
另外,Nginx 限流不能替代 Django 内部的用户级限流和业务权限控制。
15. 日志与排查
Access Log
记录 HTTP 访问情况:
access_log /var/log/nginx/access.log;
通常包括:
- 请求 IP。
- 请求方法与 URI。
- HTTP 状态码。
- 响应大小。
- 请求耗时(取决于日志格式)。
Error Log
error_log /var/log/nginx/error.log warn;
记录代理失败、连接错误、部分上游错误和 Nginx 本身的错误。
Docker 日志
官方 Nginx Docker 镜像通常已将访问日志和错误日志连接到 stdout/stderr。
可以使用:
docker compose logs -f nginx
查看容器日志。
配置测试
nginx -t
检查配置语法及相关文件。
查看实际加载的完整配置:
nginx -T
重新加载配置:
nginx -s reload
Docker 环境:
docker compose exec nginx nginx -t
docker compose exec nginx nginx -s reload
16. Nginx 与 Django 的职责
| 功能 | Nginx | Django |
|---|---|---|
| HTTPS / TLS | 主要负责 | 通常不直接处理 |
| 静态文件 | 负责提供 | 负责构建或收集 |
| 反向代理 | 负责 | 接收代理请求 |
| HTTP 压缩 | 通常负责 | 可选 |
| HTTP 缓存 | 静态资源与代理缓存 | 业务缓存 |
| API 路由 | 转发 | 业务路由 |
| Authentication | 通常不负责 | 负责 |
| Authorization | 通常不负责 | 负责 |
| 文件上传大小限制 | 第一层限制 | 业务级验证 |
| 请求限流 | IP/请求级限流 | 用户/业务级限流 |
| 后台任务 | 不负责 | Celery 等任务系统 |
总结
Nginx 最重要的知识可以归纳为:
| 模块 | 核心概念 |
|---|---|
| 配置结构 | events、http、server、location |
| 请求路由 | server_name、location、proxy_pass |
| 静态文件 | root、alias、try_files |
| 网络安全 | TLS、Real IP、限流 |
| 性能优化 | gzip/Brotli、Cache-Control、Buffering |
| 长连接 | WebSocket、SSE |
| 运维 | Access Log、Error Log、nginx -t、Reload |
对于 React + Django + Docker Compose 项目,最值得掌握:
location匹配和proxy_pass路径处理。- Nginx 与 Django 之间的 Header 传递和代理信任配置。
- React SPA 的
try_files处理。 - 静态资源长期缓存与 HTML 重新验证。
- gzip/Brotli 压缩。
- HTTP 请求超时、Buffering 和 SSE。
- Docker 环境中的日志与配置检查。