Nginx 反向代理配置完全指南 - 实战配置与优化
Nginx Reverse Proxy Complete Setup Guide
引言:为什么需要反向代理
现代 Web 服务几乎很少由单台服务器独立运营。前端通常使用 React 或 Vue 构建为静态文件进行分发,后端则由 Node.js、Python、Java、Go 等编写的应用服务器负责。再加上数据库、缓存、消息队列,整个服务就会成为由数十个组件组成的复杂系统。
此时,Nginx 反向代理就在所有这些组件前端扮演着流量指挥者的角色。用户仅通过 https://example.com 一个入口访问,但 Nginx 会根据请求路径和主机名将流量路由到合适的后端。由此,SSL 终止、负载均衡、缓存、安全、压缩、监控等都可以在中心统一管理。
本指南将涵盖从 Nginx 反向代理的概念到实战配置、优化、问题排查等实际工作中所需的全部内容。我们将通过 Node.js、Python、Java 等多种后端对接示例,以及 WebSocket、gRPC 等特殊协议的处理方式,并结合实际配置文件详细讲解运营中常遇到的问题的解决方案。
1. 反向代理 vs 正向代理
1.1 概念区别
正向代理(Forward Proxy)位于客户端一侧,代替客户端向外部服务器发起请求。公司的防火墙、VPN、学校的互联网过滤等都是典型示例。客户端知道代理的存在,但服务器并不知道代理的存在。
反向代理(Reverse Proxy)位于服务器一侧,代替服务器接收客户端请求。客户端会把代理视为真正的服务器,并不知道隐藏在代理后面的真实服务器(后端)的存在。
| 分类 | 正向代理 | 反向代理 |
|---|---|---|
| 位置 | 客户端一侧 | 服务器一侧 |
| 代理对象 | 代理客户端 | 代理服务器 |
| 主要用途 | 访问控制、缓存、匿名化 | 负载均衡、SSL 终止、缓存 |
| 代表示例 | Squid、Privoxy | Nginx、HAProxy、Traefik |
1.2 反向代理的主要优势
- 单一入口:将多个后端服务器整合到一个域名/IP 下
- SSL 终止:在代理处集中管理 HTTPS 处理,后端可使用 HTTP
- 负载均衡:将流量分散到多个后端
- 缓存:减少后端负载,提高响应速度
- 压缩:在代理处处理 Gzip/Brotli 压缩
- 安全性:防止后端服务器直接暴露,可应用 WAF
- 速率限制:在中心统一管理请求限制
- 日志集中化:所有请求在一处进行日志记录
2. proxy_pass 基本用法
2.1 最基础的代理配置
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://127.0.0.1:3000;
}
}
仅凭这样就能实现基本的反向代理。但在实际工作中,至少必须设置以下头信息:
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://127.0.0.1:3000;
# 传递原始主机信息
proxy_set_header Host $host;
# 客户端真实 IP(后端记录日志时使用)
proxy_set_header X-Real-IP $remote_addr;
# 代理链信息
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 原始协议 (http/https)
proxy_set_header X-Forwarded-Proto $scheme;
# 原始端口
proxy_set_header X-Forwarded-Port $server_port;
# 原始主机
proxy_set_header X-Forwarded-Host $host;
}
}
proxy_set_header Host $host;,Nginx 会默认将 proxy_pass 中指定的主机(这里是 127.0.0.1:3000)作为 Host 头传递。这会在使用虚拟主机路由的后端引发问题。
2.2 proxy_pass URL 中斜杠的区别
proxy_pass URL 末尾是否带有斜杠(/),行为会完全不同。这是 Nginx 初学者最容易混淆的部分:
# 情况 1:proxy_pass 无斜杠
# /api/users → http://backend/api/users (原样传递路径)
location /api/ {
proxy_pass http://backend;
}
# 情况 2:proxy_pass 带斜杠
# /api/users → http://backend/users (移除 location 路径)
location /api/ {
proxy_pass http://backend/;
}
# 情况 3:路径替换
# /api/users → http://backend/v1/users (/api/ 被替换为 /v1/)
location /api/ {
proxy_pass http://backend/v1/;
}
• 无斜杠 = 原样传递路径
• 带斜杠 = 截断 location 路径,仅传递剩余部分
如果不能明确理解这个差异,就会出现 404 错误或错误的路由。
3. 各后端的代理配置
3.1 Node.js (Express、Next.js)
# Node.js 应用(默认端口 3000)
upstream nodejs_backend {
server 127.0.0.1:3000;
keepalive 32;
}
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://nodejs_backend;
proxy_http_version 1.1;
proxy_set_header Connection ""; # 使用 keepalive
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;
# 为了 Node.js 的 req.ip,需要 trust proxy 设置
# app.set('trust proxy', true);
proxy_read_timeout 300;
proxy_connect_timeout 75;
}
}
Next.js 特化配置
server {
listen 80;
server_name next.example.com;
# Next.js 静态文件(构建时生成)
location /_next/static/ {
proxy_pass http://127.0.0.1:3000;
proxy_cache_valid 200 60m;
add_header Cache-Control "public, max-age=31536000, immutable";
}
# 图像优化 API
location /_next/image {
proxy_pass http://127.0.0.1:3000;
proxy_cache_valid 200 60m;
}
# 其余请求
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
3.2 Python (Django、Flask、FastAPI)
# 使用 Gunicorn 运行的 Django/Flask/FastAPI
upstream python_backend {
server 127.0.0.1:8000 fail_timeout=30s;
}
server {
listen 80;
server_name py.example.com;
# 静态文件由 Nginx 直接提供(Django collectstatic 等)
location /static/ {
alias /var/www/example/static/;
expires 30d;
access_log off;
}
location /media/ {
alias /var/www/example/media/;
expires 7d;
}
location / {
proxy_pass http://python_backend;
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;
# Python 应用响应时间通常可能较长
proxy_read_timeout 120;
proxy_connect_timeout 75;
# Django: SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
# Flask: app.wsgi_app = ProxyFix(app.wsgi_app, x_proto=1, x_host=1)
}
}
使用 Unix 套接字(性能提升)
# Gunicorn 以 Unix 套接字方式运行的情况
# gunicorn app:app --bind unix:/tmp/gunicorn.sock
upstream python_backend {
server unix:/tmp/gunicorn.sock;
}
server {
listen 80;
server_name py.example.com;
location / {
proxy_pass http://python_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
3.3 Java (Spring Boot、Tomcat)
# Spring Boot 应用(默认端口 8080)
upstream spring_backend {
server 127.0.0.1:8080;
keepalive 32;
}
server {
listen 80;
server_name java.example.com;
client_max_body_size 50M; # Java 应用经常有较大的载荷
location / {
proxy_pass http://spring_backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
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;
# Spring Boot application.properties:
# server.forward-headers-strategy=native
# server.tomcat.remote-ip-header=X-Forwarded-For
# server.tomcat.protocol-header=X-Forwarded-Proto
proxy_read_timeout 300;
proxy_buffer_size 16k;
proxy_buffers 8 16k;
}
}
3.4 PHP-FPM(单独章节)
PHP 使用 fastcgi_pass 而非 proxy_pass。严格来说这不是反向代理,而是 FastCGI 代理:
server {
listen 80;
server_name php.example.com;
root /var/www/php-app/public;
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
4. WebSocket 代理
WebSocket 使用 HTTP 1.1 的 Upgrade 机制。使用一般的 HTTP 代理配置,WebSocket 将无法正常工作,需要如下特殊配置:
# WebSocket 代理基本配置
server {
listen 80;
server_name ws.example.com;
location / {
proxy_pass http://127.0.0.1:8080;
# WebSocket 必需设置
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# WebSocket 保持长时间连接
proxy_read_timeout 86400s; # 24 小时
proxy_send_timeout 86400s;
# 禁用缓冲(实时双向通信)
proxy_buffering off;
}
}
4.1 HTTP 和 WebSocket 同时处理
# 如同 Socket.IO 一样在同一端口处理 HTTP 和 WebSocket 的情况
# $connection_upgrade 映射定义 (http 块)
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
server_name chat.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_read_timeout 86400s;
}
# 仅将 WebSocket 分离到单独路径的情况
location /socket.io/ {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
}
5. 微服务路由
将多个后端服务整合到单一域名,是反向代理最强大的应用场景。
5.1 基于路径的路由
# upstream 定义
upstream frontend { server 127.0.0.1:3000; }
upstream api_service { server 127.0.0.1:4000; }
upstream auth_service { server 127.0.0.1:5000; }
upstream upload_service { server 127.0.0.1:6000; }
server {
listen 80;
server_name example.com;
# 前端 (SPA)
location / {
proxy_pass http://frontend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# API 服务
location /api/ {
proxy_pass http://api_service/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
}
# 认证服务
location /auth/ {
proxy_pass http://auth_service/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
# 认证服务的速率限制应更严格
limit_req zone=auth_limit burst=5 nodelay;
}
# 上传服务(处理大文件)
location /upload/ {
proxy_pass http://upload_service/;
proxy_set_header Host $host;
# 上传允许大 body
client_max_body_size 500M;
# 设置较长的超时时间
proxy_read_timeout 300s;
proxy_send_timeout 300s;
# 禁用缓冲(流式上传)
proxy_request_buffering off;
}
}
5.2 基于主机名的路由(子域名)
# 将各子域名路由到不同的服务
server {
listen 80;
server_name app.example.com;
location / { proxy_pass http://127.0.0.1:3000; proxy_set_header Host $host; }
}
server {
listen 80;
server_name api.example.com;
location / { proxy_pass http://127.0.0.1:4000; proxy_set_header Host $host; }
}
server {
listen 80;
server_name admin.example.com;
location / {
allow 10.0.0.0/8;
deny all;
proxy_pass http://127.0.0.1:5000;
proxy_set_header Host $host;
}
}
6. 负载均衡集成
当运行多个后端实例时,可以在反向代理中自然地实现负载均衡。
# 多个后端实例
upstream api_cluster {
# 负载均衡方式(未指定时为 round-robin)
least_conn;
server 10.0.0.10:8080 weight=3 max_fails=3 fail_timeout=30s;
server 10.0.0.11:8080 weight=2 max_fails=3 fail_timeout=30s;
server 10.0.0.12:8080 weight=1 max_fails=3 fail_timeout=30s;
server 10.0.0.13:8080 backup; # 备份服务器
server 10.0.0.14:8080 down; # 维护中
keepalive 32; # 每个 worker 保持的空闲连接数
}
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://api_cluster;
proxy_http_version 1.1;
proxy_set_header Connection "";
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_next_upstream error timeout http_500 http_502 http_503 http_504;
proxy_next_upstream_tries 3;
proxy_next_upstream_timeout 10s;
}
}
7. 代理缓存
利用反向代理的缓存功能,可以大幅降低后端的负载。
# 在 http 块中定义缓存区域
http {
proxy_cache_path /var/cache/nginx/api
levels=1:2
keys_zone=api_cache:10m
max_size=1g
inactive=60m
use_temp_path=off;
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://api_backend;
proxy_set_header Host $host;
# 启用缓存
proxy_cache api_cache;
proxy_cache_key "$scheme$request_method$host$request_uri";
# 缓存有效时间
proxy_cache_valid 200 302 10m; # 成功响应缓存 10 分钟
proxy_cache_valid 404 1m; # 404 缓存 1 分钟
proxy_cache_valid any 30s; # 其他缓存 30 秒
# 缓存锁(并发请求时仅调用一次后端)
proxy_cache_lock on;
proxy_cache_lock_timeout 5s;
# 缓存使用条件
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
proxy_cache_background_update on;
# 缓存状态头(用于调试)
add_header X-Cache-Status $upstream_cache_status;
# 缓存跳过条件
proxy_cache_bypass $cookie_nocache $arg_nocache;
proxy_no_cache $cookie_nocache $arg_nocache;
}
}
}
X-Cache-Status 头的值可以确认缓存的工作情况。•
HIT - 从缓存响应•
MISS - 缓存中没有,调用后端•
EXPIRED - 缓存已过期•
BYPASS - 缓存跳过条件•
UPDATING - 缓存更新中(返回 stale 响应)
8. 缓冲与超时调优
8.1 理解缓冲
Nginx 默认会对后端响应进行缓冲。这种方式将后端的响应收集到内存(或磁盘)后再传递给客户端。这在防止因慢速客户端而长时间占用后端连接方面起着重要作用。
location / {
proxy_pass http://backend;
# 响应缓冲设置
proxy_buffering on; # 默认值
proxy_buffer_size 4k; # 响应头缓冲区
proxy_buffers 8 4k; # 响应体缓冲区(数量 x 大小)
proxy_busy_buffers_size 8k; # 正在发送到客户端的缓冲区
proxy_max_temp_file_size 1024m; # 临时文件最大大小
proxy_temp_file_write_size 8k;
# 请求缓冲
proxy_request_buffering on; # 默认值
client_body_buffer_size 16k;
client_max_body_size 10m;
}
8.2 流式响应(禁用缓冲)
# Server-Sent Events (SSE)、流式 API、大文件下载
location /stream/ {
proxy_pass http://backend;
# 禁用缓冲
proxy_buffering off;
proxy_request_buffering off;
# 支持分块传输
proxy_http_version 1.1;
proxy_set_header Connection "";
# 立即传递
proxy_cache off;
# 较长的超时时间
proxy_read_timeout 24h;
}
8.3 超时设置
location / {
proxy_pass http://backend;
# 后端连接超时 (TCP 握手)
proxy_connect_timeout 60s;
# 向后端发送请求的超时
proxy_send_timeout 60s;
# 等待后端响应的超时
proxy_read_timeout 60s;
}
9. 安全头与客户端保护
server {
listen 443 ssl http2;
server_name example.com;
# 安全头(应用于所有 location)
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
location / {
proxy_pass http://backend;
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;
# 隐藏后端返回的敏感头信息
proxy_hide_header X-Powered-By;
proxy_hide_header X-AspNet-Version;
proxy_hide_header Server;
}
}
9.1 速率限制与连接数限制
# 在 http 块中定义 zone
http {
# 每个 IP 每秒请求数限制
limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
limit_req_zone $binary_remote_addr zone=api:10m rate=30r/s;
# 每个 IP 的并发连接数限制
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
server {
location / {
limit_req zone=general burst=20 nodelay;
limit_conn conn_limit 10;
proxy_pass http://backend;
}
location /login {
limit_req zone=login burst=3 nodelay;
proxy_pass http://auth_backend;
}
location /api/ {
limit_req zone=api burst=50 nodelay;
proxy_pass http://api_backend;
}
}
}
10. 实战问题排查
10.1 504 Gateway Timeout
原因:后端响应超过了 proxy_read_timeout(默认 60 秒)
解决方法:
location / {
proxy_pass http://backend;
proxy_connect_timeout 75s;
proxy_send_timeout 300s;
proxy_read_timeout 300s; # 根据需要增加
}
10.2 502 Bad Gateway
原因:后端服务器宕机或无法访问
解决方法:
# 检查后端进程
ps aux | grep node # 或 python、java 等
# 检查端口
ss -tlnp | grep 3000
# 查看 Nginx 错误日志
tail -f /var/log/nginx/error.log | grep upstream
# 直接调用后端服务器进行测试
curl -v http://127.0.0.1:3000/
10.3 413 Request Entity Too Large
原因:上传的文件大小超过了 client_max_body_size(默认 1MB)
解决方法:
# 全局设置 (http 块)
client_max_body_size 100M;
# 或仅针对特定 location
location /upload/ {
client_max_body_size 500M;
proxy_pass http://upload_backend;
}
10.4 后端应用重定向为 HTTP
现象:以 HTTPS 连接,但后端重定向后变为 HTTP
原因:后端不知道原始协议
解决方法:
location / {
proxy_pass http://backend;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
# 或在 Nginx 层重写重定向
proxy_redirect http:// https://;
}
• Express:
app.set('trust proxy', true)• Django:
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')• Flask:
ProxyFix(app.wsgi_app, x_proto=1)• Spring Boot:
server.forward-headers-strategy=native
10.5 CORS 预检请求问题
location /api/ {
# 处理 CORS 预检请求
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '$http_origin' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, Accept' always;
add_header 'Access-Control-Max-Age' 1728000 always;
add_header 'Content-Type' 'text/plain; charset=utf-8' always;
add_header 'Content-Length' 0 always;
return 204;
}
# 实际请求
add_header 'Access-Control-Allow-Origin' '$http_origin' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
proxy_pass http://api_backend;
proxy_set_header Host $host;
}
11. 反向代理运维检查清单
- ✅ 代理头传递:设置 Host、X-Real-IP、X-Forwarded-For、X-Forwarded-Proto
- ✅ 后端框架设置:通过 trust proxy 相关配置识别原始 IP/协议
- ✅ 合适的超时设置:根据服务特性设置读取/连接/发送超时
- ✅ 上传大小限制:
client_max_body_size的适当值 - ✅ WebSocket 支持:根据需要设置 Upgrade 头
- ✅ 负载均衡故障处理:max_fails、fail_timeout、备份服务器
- ✅ 安全头:应用 HSTS、X-Frame-Options 等
- ✅ 速率限制:在敏感路径(登录、API)应用限制
- ✅ 日志分离:按服务分离代理日志与错误日志
- ✅ 监控:收集 upstream_response_time 日志
- ✅ 后端健康检查:定期的健康检查脚本
- ✅ 配置版本管理:使用 Git 管理配置文件
结论:反向代理是现代 Web 基础设施的基本功
Nginx 反向代理已超越了简单的"请求转发"功能,成为现代 Web 基础设施的核心组件。它可以将 SSL 终止、负载均衡、缓存、安全、监控等运维所需的几乎所有功能集中到一处,让后端应用程序能够专注于业务逻辑。
本指南涉及的核心要点:
- proxy_set_header 必不可少 - 务必设置 Host、X-Real-IP、X-Forwarded-* 头。
- proxy_pass 的斜杠 - 因为路径行为会完全不同,必须准确理解。
- 考虑各后端的特性 - Node.js 的 keepalive、Python 的 Unix 套接字、Java 的大缓冲区等,需要根据各自特性进行相应的配置。
- WebSocket 需要 Upgrade 头 - 普通的 HTTP 代理配置无法正常工作。
- 谨慎设置超时 - 过短会导致正常请求失败,过长会浪费资源。
- 用缓存减轻后端负担 - 大部分 GET 请求都可作为缓存目标。
- 一并考虑安全 - 代理是安全边界,务必设置速率限制和安全头。
请根据自己的服务环境调整并应用本指南中的配置示例。反向代理的配置会随着实战经验的积累而愈加精细,由此可以构建稳定且可扩展的 Web 基础设施。