Nginx 反向代理配置实战:从入门到生产环境的完整指南

很多草根站长第一次接触反向代理,是在买了好几个服务器之后:一台机器跑着网站,另一台机器跑着接口服务,或者后端程序占用了 8080、3000 这样的端口,直接暴露公网既不美观也不安全。Nginx 作为最流行的 Web 服务器,反向代理是它的核心能力之一。本文从最基础的配置讲起,覆盖常用场景、进阶技巧和排错思路,帮助你一次性把反向代理用明白。

一、什么是反向代理,为什么要用它

反向代理(Reverse Proxy)简单说,就是客户端请求先打到 Nginx,由 Nginx 根据规则把请求转发给后端的真实服务器,再把后端返回的内容回传给客户端。对客户端来说,它只跟 Nginx 打交道,根本不知道后端有几台机器、跑在什么端口。与之相对的正向代理是替客户端访问外网的,而反向代理是替服务器接收流量的,方向完全相反,别搞混。

使用反向代理的好处非常实际:一是可以隐藏后端真实地址,减少被直接攻击的面;二是可以把 HTTPS 证书统一终结在 Nginx 这一层,后端程序不用各自处理证书;三是可以在 Nginx 层做负载均衡、缓存、限流,后端只管业务逻辑;四是可以按域名或路径把一台服务器上的多个服务统一对外,实现一机多用。个人站长最常见的用法,就是把本机或内网其他机器上的 Node.js、Python、Java 服务,通过 Nginx 映射到 80/443 端口对外提供服务,或者用一台低配的机器做入口,把请求转发到内网性能更好的机器上。

二、最基础的 proxy_pass 配置

假设你有一个 Node.js 服务跑在 127.0.0.1:3000,想通过域名 api.example.com 对外提供,配置如下:

server {
    listen 80;
    server_name api.example.com;

    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_pass 指定转发目标,后面几个 proxy_set_header 是必须养成的习惯。Host 保持原始域名,否则后端拿到的 Host 是 127.0.0.1:3000,可能导致程序生成错误的链接、触发某些框架的域名校验;X-Real-IP 和 X-Forwarded-For 用于传递客户端真实 IP,否则后端日志和统计里全是 Nginx 的地址,做访问分析、封禁恶意 IP 都会失效;X-Forwarded-Proto 让后端知道用户用的是 HTTP 还是 HTTPS,程序做跳转、生成绝对链接时才不会出错。改完配置记得先 nginx -t 检查语法,确认无误再 nginx -s reload 平滑重载,不要直接重启,避免正在处理的连接被中断。

三、几个容易踩坑的细节

第一个坑是 proxy_pass 结尾的斜杠。location /api/ 配 proxy_pass http://127.0.0.1:8080/ 时,请求 /api/users 会被转成 /users,前缀被替换掉;而不带斜杠的 proxy_pass http://127.0.0.1:8080 则会原样转发为 /api/users。很多线上事故就是多一个少一个斜杠造成的,改之前一定要想清楚后端接口的路径结构,改完用 curl 实际请求一遍确认。

第二个坑是代理超时。默认的 proxy_connect_timeout 是 60 秒,proxy_read_timeout 也是 60 秒,一般够用。但如果你反代的是导出报表、批量处理、AI 接口这类耗时的服务,前端等 60 秒就超时返回 504,需要在 location 里显式调大:

proxy_connect_timeout 30s;
proxy_read_timeout 300s;
proxy_send_timeout 300s;

第三个坑是缓冲。默认 proxy_buffering 是开启的,Nginx 会先把后端响应收满再发给客户端,这对大文件下载和慢客户端很友好,后端压力也更小。但如果后端是 SSE(Server-Sent Events)这类需要实时推送的接口,必须关闭缓冲,否则数据会攒着不吐,客户端等半天收不到消息:proxy_buffering off;。另外如果网站套了 CDN,Nginx 作为回源服务器时,注意别让代理层和 CDN 层的缓冲互相叠加,导致首屏迟迟不渲染。

四、WebSocket 反向代理

现在很多站长做在线客服、实时通知、网页终端、在线协作,都会用到 WebSocket。WebSocket 握手依赖 Upgrade 请求头,Nginx 默认不会转发,需要在 location 里显式声明:

location /ws/ {
    proxy_pass http://127.0.0.1:9501;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_read_timeout 3600s;
}

proxy_http_version 1.1 必须设置,因为 HTTP/1.0 不支持 Upgrade 机制;Connection 改成 upgrade 告诉后端这是长连接。proxy_read_timeout 要调大,否则空闲连接超过 60 秒就会被 Nginx 掐断,客户端会莫名其妙掉线重连。如果后端是 Python 的 websockets 库或者 Node.js 的 ws 库,记得后端进程本身也要开足够大的超时和并发限制,否则连接数一多,后端的文件描述符先耗尽。

五、多台后端做负载均衡

流量上来以后,一台后端扛不住,可以用 upstream 定义一组服务器,让 Nginx 自动分发:

upstream backend {
    server 10.0.0.11:8080 weight=3;
    server 10.0.0.12:8080 weight=1;
    server 10.0.0.13:8080 backup;
    keepalive 32;
}

server {
    listen 80;
    server_name app.example.com;
    location / {
        proxy_pass http://backend;
    }
}

weight 控制权重,性能好的机器多分流量;backup 表示备用节点,平时不参与分发,主节点全部挂了才顶上。默认的调度算法是轮询,此外还有 ip_hash(同一 IP 固定打到同一台后端,适合有会话状态、依赖本地 Session 的程序)和 least_conn(转发给当前连接数最少的后端,适合请求处理时间差异大的场景)。keepalive 是给后端的长连接池,能显著减少 TCP 握手开销,配合 upstream 使用时记得在 location 里也加 proxy_http_version 1.1,否则 keepalive 不生效。

健康检查方面,最简单的做法是设置 fail_timeout 和 max_fails,默认 10 秒内失败 2 次就把节点标记为不可用,等 fail_timeout 过后再试探恢复。商业版 Nginx 有主动健康检查,开源版靠被动检查也基本够个人站点使用。注意:如果后端程序本身有状态(比如用户登录状态存在本地内存里),做负载均衡前务必先解决会话共享问题,否则用户刷新一次就掉一次登录,最省事的方案是上 Redis 存 Session。

六、代理层做 HTTPS 终结

证书统一放在 Nginx 层,是反向代理最省心的用法。后端内网通信走 HTTP,对外统一 443:

server {
    listen 443 ssl;
    server_name www.example.com;
    ssl_certificate /etc/nginx/ssl/example.pem;
    ssl_certificate_key /etc/nginx/ssl/example.key;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header X-Forwarded-Proto https;
    }
}

server {
    listen 80;
    server_name www.example.com;
    return 301 https://$host$request_uri;
}

注意把 80 端口全部 301 到 HTTPS,避免用户走明文,也避免搜索引擎收录 HTTP 和 HTTPS 两套重复页面。证书可以用 Let's Encrypt 免费申请,配合 certbot 的 --nginx 插件自动续期,站长只需要在 crontab 里加一条续期任务。如果后端程序需要判断用户是否来自 HTTPS,靠的就是前面设置的 X-Forwarded-Proto 头,程序里读这个头做判断即可,千万不要在后端再配一套证书,白白增加维护成本。

七、反代多个服务:一机多用

个人站长手里通常有好几个项目:一个博客、一个工具站、一个接口服务。用反向代理可以全部挂在同一台服务器的 80/443 端口上,按域名区分:

server {
    listen 443 ssl;
    server_name blog.example.com;
    root /var/www/blog;
    # 静态博客直接本地服务,无需反代
}

server {
    listen 443 ssl;
    server_name api.example.com;
    location / {
        proxy_pass http://127.0.0.1:3000;
    }
}

server {
    listen 443 ssl;
    server_name tools.example.com;
    location / {
        proxy_pass http://127.0.0.1:8080;
    }
}

这样一台 VPS 就能同时跑多个站点,互不干扰,省下好几台服务器的钱。想给某个服务单独加限流,就在对应的 server 块里配 limit_req;想给某个接口加访问认证,用 Nginx 自带的 auth_basic 就能挡一层,比在应用里写认证逻辑省事得多。

八、常见故障排查

502 Bad Gateway 是最常见的错误,意思是 Nginx 连不上后端。先用 curl 直接访问后端地址确认服务是否活着,再看后端是否只监听了 127.0.0.1 而 Nginx 从别的地址访问(IPv6 的 ::1 和 IPv4 的 127.0.0.1 是两回事,很多人栽在这里),最后看 Nginx 错误日志 /var/log/nginx/error.log 里的具体报错,比如 connect() failed (111: Connection refused) 就说明端口没监听,Connection timed out 则要考虑防火墙。504 Gateway Timeout 是后端处理太慢,按前面说的调大 proxy_read_timeout,同时排查后端自身的性能问题,比如数据库慢查询、进程阻塞。

还有一种隐蔽问题:反代之后网站出现大量 403 或者被判断为异常访问。这通常是后端程序根据客户端 IP 做校验,而你没有正确传递 X-Real-IP,后端看到的所有请求都来自 Nginx。检查后端日志里的 IP 是不是真实用户 IP,不是的话补上对应的 proxy_set_header。如果网站套了 CDN,还要把 CDN 的回源 IP 段加入可信列表,并且只信任最后一跳的 X-Forwarded-For,否则攻击者可以伪造这个头绕过 IP 封禁。另外提醒一句,反代配置改完一定要用 curl -I 实测一遍返回头,确认状态码、Server 头、缓存头都符合预期,不要只看页面能打开就完事。

九、写在最后

反向代理是 Nginx 的看家本领,也是个人站长从单机部署走向多服务架构的必经之路。掌握 proxy_pass、请求头传递、WebSocket、负载均衡这四个核心点,再加上一套排错方法论,绝大多数场景都能应付。建议你在本地用虚拟机或者云服务器上开两台机器,把本文的配置亲手敲一遍,故意制造几个 502、504 再回来对照排查清单,比看十遍教程都管用。配置文件的版本管理也别忽略,把 nginx 目录纳入 Git,每次改动留个记录,出问题随时可以回滚。

Last modification:August 27th, 2026 at 08:13 am

Leave a Comment