← 编程返回顶部

Nginx:Web Server 与 Reverse Proxy

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连接事件处理
httpHTTP 服务的公共配置
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:通常只定义 server Block,由主配置通过 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)$不区分大小写的正则匹配

匹配优先级

基本规则:

  1. 精确匹配 = 优先。
  2. 找到最长前缀匹配。
  3. 如果最长前缀使用 ^~,直接选择它。
  4. 否则按照配置顺序检查正则 Location,第一个匹配的正则生效。
  5. 没有正则匹配时,使用之前找到的最长前缀。

注意: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 的职责

功能NginxDjango
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 项目,最值得掌握:

  1. location 匹配和 proxy_pass 路径处理。
  2. Nginx 与 Django 之间的 Header 传递和代理信任配置。
  3. React SPA 的 try_files 处理。
  4. 静态资源长期缓存与 HTML 重新验证。
  5. gzip/Brotli 压缩。
  6. HTTP 请求超时、Buffering 和 SSE。
  7. Docker 环境中的日志与配置检查。

参考文档