结论: 服务器配置反向代理最简单可靠的方式是用 Nginx:在 server 块中用 proxy_pass 把请求转发到后端服务地址,并补上 Host、X-Real-IP、X-Forwarded-For、X-Forwarded-Proto 四个转发头,业务端才能拿到真实客户端 IP 与协议。Nginx 1.26 上完成基础配置只需 15 行,WebSocket 需额外加 Upgrade 头,多后端则用 upstream 做负载均衡,配置后用 nginx -t 校验并 systemctl reload nginx 生效(截至 2026 年,Nginx 稳定主线为 1.26.x/1.28.x)。
为什么要在服务器上配置反向代理
很多人的服务器一开始是直接把 Tomcat、Node.js、Python 应用监听在 80 或 443 端口对外提供服务。这种直连方式很快会遇到问题:一台机器要跑三个服务,端口怎么分?SSL 证书要在每个应用里配一遍吗?后端进程崩了谁来兜底返回友好页?
反向代理(Reverse Proxy)就是解决这类问题的标准答案。它站在客户端和后端服务之间,对外统一暴露 80/443 端口,对内按域名或路径把请求转发给不同的后端进程。客户端只认识这台 Nginx,完全不知道后端跑的是什么、在哪台机器、几个实例。
具体能拿到四个直接收益:一是端口收敛,对外只有一个入口,防火墙只需放行 80 和 443;二是 SSL 终结(TLS Termination),证书只在 Nginx 上配一次,后端走明文 HTTP,省掉各语言重复配置证书的麻烦;三是横向扩展,upstream 里加几行就能把流量分给多个后端实例;四是缓冲与容错,Nginx 会先把后端响应缓冲完再慢慢发给慢速客户端,避免后端进程被慢连接拖死。
和它容易混淆的是正向代理(Forward Proxy):正向代理代理的是客户端,服务端不知道真实客户是谁;反向代理代理的是服务端,客户端不知道真实服务是谁。 CDN、API 网关、Ingress Controller 本质上都是反向代理的特化形态。
配置前的准备工作
动手前先确认四件事,绝大多数"配完不生效"的问题都出在这四步没做扎实。
- Nginx 已安装且版本明确:执行
nginx -v,截至 2026 年主流发行版仓库提供 1.24~1.28,Ubuntu 24.04 LTS 默认源为 1.24,官方源可装到 1.26 以上。 - 后端服务已在本地监听:反向代理只转发,不负责拉起后端。先用
ss -lntp | grep 8080确认后端在 127.0.0.1:8080 正常监听。 - 后端建议只监听回环地址:配好代理后,后端应改监听
127.0.0.1,避免用户绕过 Nginx 直接访问源端口。 - 防火墙与安全组放行 80/443:云服务器还要在控制台安全组里放行,只改系统防火墙不够。
安装 Nginx 的命令如下:
# Ubuntu / Debian 24.04 / 12
sudo apt update && sudo apt install -y nginx
# RHEL / Rocky / AlmaLinux 9
sudo dnf install -y nginx
# 启动并设为开机自启
sudo systemctl enable --now nginx
# 确认版本与运行状态
nginx -v
systemctl status nginx --no-pagerUbuntu 系的主配置在 /etc/nginx/nginx.conf,站点配置放在 /etc/nginx/conf.d/*.conf 或 /etc/nginx/sites-available/(软链到 sites-enabled/);RHEL 系主配置在 /etc/nginx/nginx.conf,站点通常直接放 /etc/nginx/conf.d/。本文示例以 /etc/nginx/conf.d/ 为例。
最小可用的反向代理配置
结论先行:一个能跑的基础反向代理配置只需要一个 server 块加一个 location 块里的 proxy_pass。先把最小版本跑通,再逐步加转发头、超时和负载,是排查成本最低的路径。
假设后端是一个跑在 127.0.0.1:8080 的 Java 应用,对外域名 app.example.com:
# /etc/nginx/conf.d/app.conf
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://127.0.0.1:8080;
}
}写完校验并重载:
sudo nginx -t # 语法校验,出现 syntax is ok 才继续
sudo systemctl reload nginx # 热重载,不断连接
curl -I http://app.example.com # 验证返回头这里有一个新手必踩的坑:proxy_pass 后面带不带路径,行为完全不同。
| 写法 | 请求 /api/user | 转发给后端的路径 | 说明 |
|---|---|---|---|
proxy_pass http://127.0.0.1:8080; | /api/user | /api/user | 不带 URI,路径原样透传 |
proxy_pass http://127.0.0.1:8080/; | /api/user | /api/user | 尾部只有斜杠,等价于替换 location 前缀后拼接 |
proxy_pass http://127.0.0.1:8080/v1/; | /api/user | /v1/user | 带 URI 时,location 匹配部分被替换为 /v1/ |
location /api/ { proxy_pass http://127.0.0.1:8080/; } 这种写法会把 /api/user 变成 /user 再转发——想保留 /api 前缀就别在 proxy_pass 末尾加斜杠,或者用 rewrite。这是"配完返回 404"最常见的原因。
必须补的四个转发头
结论先行:不加转发头,后端应用看到的来源 IP 全是 127.0.0.1,HTTPS 页面里生成的链接会变成 http://,这两个问题只有靠 proxy_set_header 解决。
Nginx 默认只转发两个头(Host 和 Connection),其余需显式声明。生产环境的标准做法是先在 http 块里定义一个可复用的配置文件:
# /etc/nginx/conf.d/proxy_headers.conf 放在 http 块内被 include
proxy_http_version 1.1;
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 Connection "";然后在站点里引用:
server {
listen 80;
server_name app.example.com;
include /etc/nginx/conf.d/proxy_headers.conf;
location / {
proxy_pass http://127.0.0.1:8080;
}
}各参数含义如下:
Host $host:把原始域名传给后端。后端做虚拟主机路由、生成绝对 URL 都依赖它,不传则后端收到的是127.0.0.1:8080。X-Real-IP $remote_addr:直连 Nginx 的客户端 IP。只有一级代理时它等于真实用户 IP。X-Forwarded-For $proxy_add_x_forwarded_for:追加式链路记录,格式为"客户端IP, 代理1, 代理2",多级代理下取第一个逗号前的值才是真实 IP。X-Forwarded-Proto $scheme:告知后端用户用的是 http 还是 https。Spring Boot 需要配合server.forward-headers-strategy=framework,Nginx 侧还需告诉 PHP$_SERVER['HTTPS']=on或用fastcgi_param。
后端取真实 IP 的语言实现差别较大,举两例:
# Django settings.py:需声明可信代理跳数,否则 X-Forwarded-For 不生效
USE_X_FORWARDED_HOST = True
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')WebSocket、超时与 upstream 负载均衡
结论先行:WebSocket 和长连接必须显式发送 Upgrade/Connection 头并调大超时,否则必然握手失败或 60 秒断连;多副本后端则用 upstream 定义服务器组。
WebSocket 代理需要识别客户端发来的 Upgrade 请求头:
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
server_name ws.example.com;
include /etc/nginx/conf.d/proxy_headers.conf;
location /ws {
proxy_pass http://127.0.0.1:9000;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_read_timeout 3600s; # WebSocket 空闲超时,默认 60s 会频繁断开
proxy_send_timeout 3600s;
}
}超时参数必须单独调,Nginx 默认 proxy_read_timeout 为 60 秒,导出报表、AI 推理这类慢接口到点就被切断并返回 504:
location / {
proxy_pass http://127.0.0.1:8080;
proxy_connect_timeout 10s; # 与后端建连超时,内网建议 5~10s
proxy_send_timeout 60s; # 向后端发送请求超时
proxy_read_timeout 120s; # 等待后端响应超时,慢接口按需调大
proxy_buffering on; # 缓冲后端响应,避免慢客户端拖住后端进程
proxy_buffer_size 16k;
proxy_buffers 8 16k;
}多台后端时用 upstream 做负载均衡(Load Balancing):
upstream app_backend {
least_conn; # 最少连接,长连接场景优于轮询
server 127.0.0.1:8081 weight=3 max_fails=3 fail_timeout=30s;
server 127.0.0.1:8082 weight=1 max_fails=3 fail_timeout=30s;
server 10.0.0.11:8080 backup; # 备用机,主节点全挂才启用
keepalive 32; # 到后端的长连接池
}
server {
listen 80;
server_name app.example.com;
include /etc/nginx/conf.d/proxy_headers.conf;
location / {
proxy_pass http://app_backend; # 注意:这里写 upstream 名称
proxy_next_upstream error timeout http_502 http_503 http_504;
proxy_next_upstream_tries 2;
}
}调度策略选型:
| 策略 | 指令 | 适用场景 |
|---|---|---|
| 轮询 | round_robin(默认) | 后端规格一致、请求耗时均匀 |
| 加权轮询 | weight=n | 后端配置不等,按能力分配 |
| 最少连接 | least_conn | 长短请求混杂,如 WebSocket、文件上传 |
| IP 哈希 | ip_hash | 未做会话共享的老系统临时方案 |
| 一致性哈希 | hash $request_uri consistent | 缓存类反向代理,降低回源 |
配置 HTTPS 反向代理的正确姿势
结论先行:SSL 证书配在 Nginx 上终止 TLS,后端继续走 HTTP,这是反向代理的标准形态;配好后建议加 HTTP 到 HTTPS 的 301 跳转。
server {
listen 80;
server_name app.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
http2 on;
server_name app.example.com;
ssl_certificate /etc/letsencrypt/live/app.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_cache shared:SSL:10m;
include /etc/nginx/conf.d/proxy_headers.conf;
location / {
proxy_pass http://app_backend;
}
}用 Certbot 签发免费证书(有效期 90 天,Certbot 会自动配置续期定时器):
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d app.example.com
sudo certbot renew --dry-run # 验证自动续期链路是否通畅常见误区 / 排错提示
proxy_pass尾部斜杠导致路径丢失:location /api/ { proxy_pass http://127.0.0.1:8080/; }会把/api/user改写成/user。保留前缀的写法是不加尾部斜杠。- 转发头只在
server层写却被子location覆盖:子块一旦出现任何proxy_set_header,父级同名指令全部失效,必须在该location里重新声明一遍。这是"后端永远只拿到 127.0.0.1"的头号原因。 proxy_pass写 hostname 但没配 resolver:proxy_pass http://backend.internal;中的域名只在 Nginx 启动时解析一次,后端 IP 变了就一直失败。应写 IP,或配resolver 10.0.0.2 valid=30s;并用变量触发动态解析。- 忘了 SELinux:RHEL 系上 Nginx 转发到本地端口会被 SELinux 拦截,日志出现
Permission denied。放行命令:setsebool -P httpd_can_network_connect 1。 - 改完直接 restart:
systemctl restart nginx会断开存量连接,reload才是热生效。但改了监听端口必须 restart。 - 忽略后端健康检查:
upstream默认只是被动摘除(靠max_fails),首次失败仍会打到坏节点。OpenResty 或 Nginx Plus 才有主动健康检查,开源版建议配外部探活脚本。
企业QQ咨询




