Nginx 反向代理实战:从 502、跨域到 HTTPS,一次打通
502 不是玄学,是 Nginx 在告诉你:上游服务它连不上。
前后端分离之后,前端跑在 80 端口,后端在 3000,浏览器跨域、微信回调写死域名、HTTPS 还得配证书------这些问题我全用 Nginx 反向代理一把梭解决了。这篇从"它到底在干嘛"讲起,把 502/504/413、跨域、HTTPS 和那些反直觉的坑一次讲清。
什么时候你真的需要它?三种典型场景:一是单台服务器上同时跑前端和多个后端服务,靠不同路径分发;二是微信、支付宝等回调只认 80/443 端口,必须做一层转发;三是想统一收口 HTTPS 和静态资源缓存。如果你只是本地 npm run dev 调试,那用不到,但只要你把项目部署到公网,基本都绕不开这一层。下面所有配置我都标了中文注释,复制改域名即可用。
一、反向代理到底在干什么
一句话:反向代理是站在服务端的,把用户请求悄悄转发给后端真实服务,再把结果回传给用户,用户全程不知道背后有几台机器。
| 维度 | 正向代理 | 反向代理 |
|---|---|---|
| 代理对象 | 客户端(帮内网用户访问外网) | 服务器(帮后端隐藏真实地址) |
| 谁配置 | 客户端自己配 | 服务端运维配 |
| 典型场景 | 内网出口、访问控制 | 负载均衡、统一入口、SSL 卸载 |
| 用户感知 | 知道自己用了代理 | 以为就在访问这一台机器 |
我们说的"反向代理"就是后者:用户只访问 Nginx 的 80/443,Nginx 把 /api 转给后端 3000 端口。
二、最小可用的 proxy_pass
先装好:sudo apt install nginx(CentOS 用 yum install nginx)。配置文件放在 /etc/nginx/conf.d/,默认 nginx.conf 会 include 这个目录。最小能跑的反代:
nginx
server {
listen 80;
server_name example.com; # 改成你的域名,没域名先用服务器 IP
location / {
proxy_pass http://127.0.0.1:3000; # 转发到本机后端
}
}
改完务必先校验再生效:
bash
sudo nginx -t # 检查语法,看到 successful 再继续
sudo nginx -s reload # 平滑重载,不影响已有连接
三、四个关键 header 为什么必须传
只写 proxy_pass 会埋坑:后端拿到的 Host 是 127.0.0.1:3000,拿不到用户真实 IP,生成绝对链接、限流、日志全错。所以标准写法一定要带这四个头:
nginx
location / {
proxy_pass http://127.0.0.1:3000;
# 把原始域名传给后端,后端生成链接才不会写成 localhost
proxy_set_header Host $host;
# 用户真实 IP,后端日志、风控要用
proxy_set_header X-Real-IP $remote_addr;
# 多层代理时累加的 IP 链,比 X-Real-IP 更完整
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 原始协议,后端判断是不是 HTTPS 靠它(否则死循环跳转)
proxy_set_header X-Forwarded-Proto $scheme;
}
后端框架(如 Express、Spring)开启 trust proxy 后就能正确读取这些头。
四、502/504/413 各自怎么查
502 Bad Gateway :Nginx 连上了 socket 但上游返回了无效响应,最常见是后端根本没启动或端口写错。
bash
# 先看后端进程在不在、端口是否监听
sudo lsof -i :3000
curl http://127.0.0.1:3000 # 直接在服务器本地打,排除 Nginx 干扰
tail -f /var/log/nginx/error.log # 502 会把 "connect() failed" 写这里
504 Gateway Timeout :连接成功了,但后端处理太久没回。调大超时:
nginx
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s; # 长任务 / 大查询重点调这项
413 Request Entity Too Large :上传文件超过默认 1MB 限制。改 client_max_body_size:
nginx
client_max_body_size 50m; # 放在 http / server / location 都行
定位通用套路:先 sudo nginx -t 确认配置没错,再 tail -f /var/log/nginx/error.log 看具体行号报错,最后在服务器本地 curl 后端端口验证"到底是 Nginx 的锅还是后端的锅"。
五、用 Nginx 解决跨域
前端和后端不同源时浏览器会拦跨域。与其改后端代码,不如让 Nginx 在响应里加上 CORS 头,并正确处理预检请求(OPTIONS):
nginx
location /api/ {
# 预检请求直接返回 204,不带 body
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
return 204;
}
# 正常响应也带上头
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
proxy_pass http://127.0.0.1:3000;
}
生产环境建议把 * 换成具体前端域名,更安全。
六、Certbot 一键上 HTTPS
免费证书用 Let's Encrypt + Certbot,自动申请还能自动续期:
bash
# 安装(Ubuntu/Debian 示例)
sudo apt install certbot python3-certbot-nginx
# 一条命令:申请证书 + 自动改写 Nginx 配置 + 加上 HTTP 跳转 HTTPS
sudo certbot --nginx -d example.com -d www.example.com
# 验证自动续期任务(certbot 默认会注册 systemd timer)
sudo certbot renew --dry-run
执行后 Nginx 配置里会自动多出 listen 443 ssl; 和证书路径,并新增一段 80 端口的 return 301 https://$host$request_uri;。证书 90 天一换,renew 只在快过期时才真正更新,放心挂着。
七、gzip 压缩与静态资源缓存
开启 gzip,文本类资源体积能砍掉 70%:
nginx
gzip on;
gzip_min_length 1024; # 小于 1KB 不压缩,省 CPU
gzip_comp_level 6; # 压缩级别 1-9,6 是性价比甜点
gzip_types text/plain text/css application/json application/javascript text/xml application/xml image/svg+xml;
静态资源(图片、打包后的 JS 与样式表)设长缓存,文件名带 hash 就不怕更新不生效:
nginx
location /static/ {
expires 30d;
add_header Cache-Control "public, immutable";
}
八、单机多域名多站点
一台机器挂多个域名,写多个 server 块即可,Nginx 按 server_name 路由:
nginx
server {
listen 80;
server_name a.com;
location / { proxy_pass http://127.0.0.1:3000; }
}
server {
listen 80;
server_name b.com;
location / { proxy_pass http://127.0.0.1:4000; }
}
每个站点放一个独立 .conf 文件在 conf.d/ 下,互不干扰,删站点就是删文件再 reload。
九、常见坑逐个排
坑 1:proxy_pass 结尾斜杠的差别
proxy_pass http://127.0.0.1:3000; 和 proxy_pass http://127.0.0.1:3000/; 不一样!带斜杠会把 location /api/ 里的 /api/ 部分剥掉 再转发;不带则连同 /api/ 一起转。路径对不上大多源于此,按后端实际路由选。
坑 2:上传大文件 413
忘了 client_max_body_size,加在 server 或 location 里并 reload。
坑 3:SSE / WebSocket 被缓冲
实时推送、聊天室连上却收不到消息,是 Nginx 缓冲了响应。关缓冲并升级协议:
nginx
location /ws/ {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade"; # WebSocket 必备
proxy_buffering off; # SSE 必备,别等满了才发
}
坑 4:改了配置却没生效
你改的是 conf.d/ 下的文件却忘了 nginx -s reload,或改错了文件(确认 nginx -t 指向的配置路径)。
坑 5:SELinux 挡转发(CentOS/RHEL 特有)
配置全对但就是 502,且 error.log 报 Permission denied。放行网络转发:
bash
sudo setsebool -P httpd_can_network_connect on
收个尾:反向代理本质就是"转发 + 补头 + 兜底超时",502 先本地 curl 后端、413 改最大体积、跨域加头、HTTPS 交给 Certbot。今晚就把你那个还在裸跑 3000 端口的项目用上面的最小配置接一层 Nginx,再跑一次 certbot --nginx,体验一次输入 http:// 自动跳 https:// 的爽感。