在应用服务器前面部署 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_version1.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,然后配置应用程序以读取这些头。