在应用服务器前面部署 Nginx 是现代 Web 部署中最常见的模式之一。应用通过本地端口使用纯 HTTP 通信,而 Nginx 负责处理公共侧:TLS 终止、头部重写、缓存、压缩以及在多个后端之间进行负载均衡。
反向代理是一种服务,它接收客户端请求,将请求转发到一个或多个代理服务器,并将响应返回给客户端。由于其高性能和较小的内存占用,Nginx 经常被用作 Node.js、Python 和 Java 等 HTTP 服务器(如)以及 PHP-FPM 和 FastCGI 等非 HTTP 后端的反向代理。
使用 Nginx 作为反向代理为您提供以下优势:
- 负载均衡:Nginx 可以将客户端请求分发到多个上游服务器,从而提高性能、可扩展性和可用性。
- 缓存:Nginx 可以缓存上游响应并直接提供给客户端,从而减少后端负载并加快页面交付速度。
- TLS 终止:Nginx 可以接受 HTTPS 连接,对其进行解密,并通过纯 HTTP 与上游通信。这将 TLS 配置和证书集中在一个地方。
- 压缩:如果上游未发送压缩响应,Nginx 可以在发送给客户端之前对其进行压缩。
- 速率限制和访问控制:Nginx 可以限制每个客户端 IP 的请求数量,并根据客户端位置或请求头部(如
User-Agent和Referer)限制访问。
本指南介绍如何配置 Nginx 作为反向代理,涵盖基本语法、您几乎总是需要设置的 proxy_set_header 指令以及您可能遇到的最常见的问题。
先决条件
我们假设您已经在 Ubuntu、Debian 或其他 Linux 服务器上安装了 Nginx,并且拥有 root 或 sudo 权限来编辑 Nginx 配置。
使用 Nginx 作为反向代理
要将 Nginx 配置为 HTTP 服务器的反向代理,请打开域名的服务器块配置文件并声明一个转发到上游的 location:
nginx
server {listen 80;
server_name www.example.com example.com;
location /app {proxy_pass http://127.0.0.1:8080;}}proxy_pass 指令设置上游 URL。它接受 http 或 https 作为协议,域名或 IP 地址,以及可选的端口和 URI。
上述配置告诉 Nginx 将匹配 /app 位置的所有请求转发到 http://127.0.0.1:8080 的上游。
在 Ubuntu 和基于 Debian 的发行版上,服务器块文件位于 /etc/nginx/sites-available。在 Fedora、RHEL 及其衍生版本上,它们位于 /etc/nginx/conf.d。
要了解 location 和 proxy_pass 如何交互,请考虑以下示例:
nginx
server {listen 80;
server_name www.example.com example.com;
location /blog {proxy_pass http://node1.com:8000/wordpress/;}}如果访客访问 http://example.com/blog/my-post,Nginx 会将请求代理到 http://node1.com:8000/wordpress/my-post。
当 proxy_pass 地址包含 URI(本例中为 /wordpress/)时,在将请求发送到上游之前,会用该 URI 替换匹配的位置前缀。当 proxy_pass 地址没有 URI 时,原始请求 URI 会原样传递。
使用 proxy_set_header 设置头部
默认情况下,Nginx 在代理请求时会重写两个头部。Host 设置为上游主机($proxy_host),Connection 设置为 close。客户端发送的其他所有内容都会原样转发,空头部会被剥离。
这种默认设置通常不是您想要的。特别是,上游看到的 $proxy_host 是 Host 头部,而不是客户端请求的原始域名,它看到的是 127.0.0.1(Nginx 本身)作为远程地址。解决方法是使用 proxy_set_header 设置上游实际需要的头部。
对于正常的 HTTP 反向代理,以下块是一个很好的基线:
nginx
location / {proxy_pass http://127.0.0.1:3000;
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_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;}每个指令都有特定用途:
Host $host:转发客户端请求的原始域名。如果没有这个,上游会看到$proxy_host值,无法进行基于名称的路由或生成正确的绝对 URL。X-Real-IP $remote_addr:将真实客户端 IP 转发给上游。如果没有它,上游看到的是127.0.0.1。X-Forwarded-For $proxy_add_x_forwarded_for:将客户端 IP 附加到任何现有的X-Forwarded-For链中,以便上游可以重建请求通过多个代理的完整路径。X-Forwarded-Proto $scheme:告诉上游原始请求是http还是https。框架在生成重定向或规范 URL 时依赖于此。X-Forwarded-Host $host和X-Forwarded-Port $server_port:转发客户端请求的原始主机和端口,这在监听不同端口的上游很有用。
Websocket 反向代理
Websockets 需要一些额外的设置,因为它们使用 HTTP Upgrade 机制。如果您的应用程序提供 websocket 端点,请在 http 上下文中添加一个 map,并为该端点使用以下位置块:
nginx
map $http_upgrade $connection_upgrade {default upgrade;
'' close;}
server {#... other directives
location /socket/ {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_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;}}仅对实际处理 websocket 流量的位置使用这些 websocket 专用指令:
proxy_http_version 1.1:当您的上游需要 HTTP/1.1 功能(如 websocket 升级或持久连接)时使用此选项。Nginx 1.29.7 之前的版本默认使用 HTTP/1.0;1.29.7 及更高版本默认使用 HTTP/1.1。Upgrade $http_upgrade和Connection $connection_upgrade:websocket 升级所必需。当客户端请求升级时,map将Connection设置为upgrade,否则对于正常请求回退到close。
如果启用了 proxy_cache,请添加 proxy_cache_bypass $http_upgrade;,以便 websocket 升级请求不从缓存中提供。
要防止将头部传递给上游,将其值设置为空字符串。例如,这可以移除 Accept-Encoding 头部,使上游始终返回未压缩的响应:
nginx
proxy_set_header Accept-Encoding "";更改配置后重新加载 Nginx,以使新设置生效。请参阅 Nginx 命令参考以获取确切命令。
proxy_set_header 与 add_header
这两个指令经常被混淆:
proxy_set_header在 Nginx 发送到上游的请求上设置头部。add_header在 Nginx 返回给客户端的响应上设置头部。
当上游需要看到某些内容(客户端 IP、原始主机)时使用 proxy_set_header。当浏览器需要看到某些内容(安全头部、CORS、缓存控制)时使用 add_header。
反向代理到非 HTTP 后端
Nginx 还可以代理到不使用 HTTP 的后端。代替使用 proxy_pass,您使用一个特定于协议的指令:
fastcgi_pass:反向代理到 FastCGI 服务器(如 PHP-FPM)。uwsgi_pass:反向代理到 uWSGI 服务器。scgi_pass:反向代理到 SCGI 服务器。memcached_pass:反向代理到 Memcached 服务器。
最常见的例子是使用 Nginx 作为 PHP-FPM 的反向代理:
nginx
server {#... other directives
location ~ \.php$ {include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;}}调整套接字路径以匹配系统上安装的 PHP-FPM 版本(例如 Ubuntu 24.04 上的 php8.3-fpm.sock 或 Debian 13 上的 php8.4-fpm.sock)。
TLS 终止
今天通过 HTTPS 提供内容是默认设置。要在 Nginx 处终止 TLS 并将纯 HTTP 代理到上游,请将反向代理块包装在启用 TLS 的 server 中:
nginx
server {listen 443 ssl;
http2 on;
server_name www.example.com example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
location / {proxy_pass http://127.0.0.1:3000;
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;}}http2 on; 指令可为服务器块启用 HTTP/2。在早于 1.25.1 版本的 Nginx(例如 Ubuntu 24.04 上的默认包)中,该指令不可用;请使用 listen 443 ssl http2; 代替。
如果您还没有证书,请使用 Certbot 获取免费的 Let's Encrypt 证书并配置 Nginx。如果代理位于内部网络且从未从互联网访问,自签名证书就足够了。
快速参考
如需打印的快速参考指南,请参阅 Nginx 速查表。
常见的 proxy_set_header 指令及其通常配对的变量:
| 指令 | 值 | 目的 |
|---|---|---|
Host | $host | 转发客户端请求的域名,而非 $proxy_host |
X-Real-IP | $remote_addr | 将客户端 IP 发送给上游 |
X-Forwarded-For | $proxy_add_x_forwarded_for | 将客户端 IP 附加到任何现有链中 |
X-Forwarded-Proto | $scheme | 告知上游请求是 http 还是 https |
X-Forwarded-Host | $host | 转发客户端请求的原始主机 |
X-Forwarded-Port | $server_port | 转发客户端连接的原始端口 |
Upgrade | $http_upgrade | 用于 websocket 位置 |
Connection | $connection_upgrade | 用于 websocket 位置 |
位于 proxy_set_header 之外的辅助指令:
| 指令 | 值 | 目的 |
|---|---|---|
proxy_pass | 上游的 URL | 反向代理的目标 |
proxy_http_version | 1.1 | 当上游需要 HTTP/1.1 功能(如 websocket)时使用 |
proxy_cache_bypass | $http_upgrade | 如果启用了 proxy_cache,则绕过 websocket 升级的缓存 |
故障排除
502 Bad Gateway
Nginx 无法连接到上游。请确认上游服务正在监听 proxy_pass 中指定的地址和端口(在 Nginx 主机上使用 ss -tlnp 或 curl 检查),确保 SELinux 或防火墙未阻止连接,并在 /var/log/nginx/error.log 中查找确切原因(连接被拒绝、超时或 TLS 握手失败)。
客户端 IP 在上游显示为 127.0.0.1
您缺少 proxy_set_header X-Real-IP $remote_addr; 或 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;。请同时添加这两项,并确保应用程序从这些头读取信息。如果应用程序位于多个代理之后,您还需要配置它以信任客户端连接的那一跳。
Websocket 连接立即断开
Websockets 需要 HTTP/1.1 以及 Upgrade/Connection 头对。在提供 websocket 端点的位置中设置 proxy_http_version 1.1;、proxy_set_header Upgrade $http_upgrade; 和 proxy_set_header Connection $connection_upgrade;,并在 http 上下文中定义 map。
应用程序生成 http:// URL,即使站点是 HTTPS
由于 Nginx 终止 TLS 并通过纯 HTTP 转发,应用程序不知道原始请求是 HTTPS。请添加 proxy_set_header X-Forwarded-Proto $scheme; 并配置框架以信任它(例如 Express 中的 trust proxy,Django 中的 SECURE_PROXY_SSL_HEADER)。
上游的虚拟主机路由失效
上游将 $proxy_host 视为 Host 头,无法将其匹配到任何虚拟主机。请使用 proxy_set_header Host $host; 转发原始主机。
常见问题
如何在 Nginx 反向代理中设置头信息?
在拥有 proxy_pass 的 location 块内使用 proxy_set_header。例如,proxy_set_header Host $host; 将原始主机头转发给上游。要从代理请求中移除头信息,将其值设置为空字符串:proxy_set_header Accept-Encoding "";。
proxy_set_header 和 add_header 有什么区别?proxy_set_header 修改 Nginx 发送给上游的请求头。 add_header 修改 Nginx 返回给客户端的响应头。使用前者与上游通信,使用后者与浏览器通信(例如设置 Strict-Transport-Security 或 CORS 头)。
我应该转发哪些头信息到上游服务器?
至少转发 Host、X-Real-IP、X-Forwarded-For 和 X-Forwarded-Proto。对于 websocket,还需设置 Upgrade 和 Connection $connection_upgrade 并使用 proxy_http_version 1.1。可选但有用:当公共端点与上游地址不同时,使用 X-Forwarded-Host 和 X-Forwarded-Port。
为什么我的后端显示的客户端 IP 是 127.0.0.1?
如果没有显式的头配置,上游只能看到 Nginx 连接的地址,通常是 127.0.0.1。请使用 proxy_set_header X-Real-IP $remote_addr; 和 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 转发真实的客户端 IP,然后配置应用程序以读取这些头。