宝塔反向代理入口不在主菜单,必须先添加一个已解析域名的站点(即使为空),才能在该站点“设置”中看到“反向代理”选项卡;否则无法配置,常见502错误多因站点未建、域名未解析或目标服务未运行。

反向代理入口必须先建站点才能看见
宝塔的反向代理功能不是独立菜单,它藏在「网站」列表里某个具体站点的设置中。不先添加一个已解析域名的站点(哪怕目录空着、不放任何文件),你根本点不到「反向代理」选项卡。这点很多人卡住——以为面板没这功能,其实是漏了前置动作。
常见错误现象:502 Bad Gateway 十有八九是因为站点没建、域名没解析,或者目标服务压根没跑起来。验证方法很简单:在服务器上执行 curl -v http://127.0.0.1:端口号,能拿到响应再填进宝塔。
- 每个要代理的端口,都得单独建一个站点(比如
admin.example.com、api.example.com) - 所有站点必须绑定真实解析到本机 IP 的域名,不能用未解析的测试名
- 站点 PHP 版本选「纯静态」即可,反代和 PHP 运行无关
目标 URL 必须带协议且末尾带斜杠
填错 目标URL 是 502 最常见原因。它不是只写 127.0.0.1:3000,而是必须完整带上协议和结尾斜杠:http://127.0.0.1:3000/ 或 https://127.0.0.1:8443/。漏掉 / 会导致路径拼接错乱,比如你访问 /api/users,后端实际收到的是 /apiusers。
如果后端是 HTTPS 服务,目标 URL 必须写 https://,宝塔会自动加 proxy_ssl_verify off(内网够用)。但别指望它帮你处理证书链问题——自签或过期证书照样报错。
- 后端监听
http://127.0.0.1:3001→ 填http://127.0.0.1:3001/ - 后端监听
https://localhost:8002→ 填https://127.0.0.1:8002/(localhost在 Nginx 里可能解析失败,统一用127.0.0.1) - 别填
http://127.0.0.1:3000/api,那是错的;要代理整个服务就写/,要代理子路径才另配「代理目录」
多端口共用一个域名?用子路径代理 + location 规则
想让 example.com/admin/ 走 8001 端口、example.com/api/ 走 9000 端口,就不能靠「单站单代理」,得进配置文件手动加 location 块。宝塔内置反代只支持全量代理或单路径前缀,复杂分流必须手改。
直接在站点「配置文件」选项卡里,在 server { } 块内、root 指令下方插入:
location ^~ /admin/ {
proxy_pass http://127.0.0.1:8001/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location ^~ /api/ {
proxy_pass http://127.0.0.1:9000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
注意:proxy_pass 后面的 / 不能少,否则路径会多拼一层。
-
^~表示前缀匹配优先级最高,避免被正则规则覆盖 - 两个
location的路径不能重叠(比如/api/和/api/v1/写一起就冲突) - 改完必须点「保存」再点「重载配置」,只保存不重载等于没动
静态资源 404 和 MIME 错误怎么修
典型表现是页面 HTML 能加载,但 CSS/JS 返回 404,或控制台报 MIME type 错误。根本原因是后端返回的 HTML 里写了绝对路径(如 href="http://127.0.0.1:3000/static/app.css"),浏览器直接去请求这个地址,当然失败。
解决思路不是让后端改代码,而是补一层代理规则,把静态资源请求也转给对应后端:
location /static/ {
proxy_pass http://127.0.0.1:3000/static/;
proxy_set_header Host $host;
}
或者更彻底一点,用宝塔的「URL 重写」功能,在站点设置 → 「伪静态」里加:
rewrite ^/static/(.*)$ /static/$1 break;
但这只是兜底方案。真正健壮的做法,是让后端服务用相对路径或通过环境变量注入正确的静态资源前缀。
容易被忽略的一点:如果你开了 HTTPS 访问域名,但后端是 HTTP 服务,Nginx 默认不会把 X-Forwarded-Proto 头透传过去。某些框架(如 Django、Express)靠这个头判断是否生成 HTTPS 链接,漏了就会继续吐出 http:// 开头的资源地址,死循环。











