Nginx正向代理中,客户端带端口请求(如example.com:8080)失败的根本原因是$host自动剥离端口,必须改用$http_host;HTTP代理需配置resolver和proxy_pass http://$http_host$request_uri,HTTPS代理则须启用proxy_connect模块并显式允许目标端口。

核心问题是:Nginx 正向代理配置中,当客户端请求带端口的 URL(如 http://example.com:8080/path)时,proxy_pass 无法正确解析并转发,导致跳转失败或返回 400 错误。
关键原因:$host 不含端口,$http_host 才保留原始 Host 和端口
Nginx 的内置变量 $host 会自动剥离端口号(只取域名部分),而浏览器或 curl 发起的请求中,Host 头实际包含端口(如 example.com:8080)。若 proxy_pass 写成 http://$host$request_uri,就会丢失端口,目标服务器收不到正确 Host 头,可能拒绝服务或重定向异常。
- ✅ 正确写法必须用
$http_host—— 它直接取自请求头,完整保留域名+端口 - ❌ 避免使用
$host或$server_name,它们不反映客户端真实 Host 值 - ⚠️ 注意:
$http_host在 HTTPS 正向代理中仍有效,但需配合 CONNECT 方法(见下文)
HTTP 端口代理:用 resolver + $http_host 实现动态转发
适用于纯 HTTP 流量(非加密),且客户端能主动设置 Proxy-Authorization 或通过 curl -x 指定代理。配置示例:
server {
listen 7003;
resolver 8.8.8.8 valid=30s;
location / {
# 关键:用 $http_host 保持端口信息
proxy_pass http://$http_host$request_uri;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_connect_timeout 10s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
}
}- resolver 必须配置,否则 Nginx 无法解析带端口的域名(如 api.test:3000)
- proxy_set_header Host $http_host 确保后端收到原始 Host 头(含端口)
- 此方案不适用于浏览器直连 HTTPS 网站,仅限 HTTP 或命令行工具(curl、wget)
HTTPS 端口代理:必须启用 CONNECT 方法支持
浏览器访问 https://api.example:8443 时,先发 CONNECT 请求建立隧道。标准 Nginx 编译不支持该方法,需加载第三方模块:
- 安装
ngx_http_proxy_connect_module(注意匹配 Nginx 版本,当前支持 1.4.x–1.25.x) - 编译时添加
--add-module=./ngx_http_proxy_connect_module - 配置中启用
proxy_connect并显式允许目标端口:
server {
listen 8080;
resolver 114.114.114.114;
proxy_connect;
proxy_connect_allow 443 8443 8080; # 显式放开自定义 HTTPS 端口
proxy_connect_connect_timeout 10s;
proxy_connect_read_timeout 10s;
proxy_connect_send_timeout 10s;
}- 若漏配
proxy_connect_allow,Nginx 会拒绝 CONNECT 到非 443 端口的请求(报 ERR_TUNNEL_CONNECTION_FAILED) - DNS 解析器(resolver)仍不可少,否则无法解析
api.example:8443中的域名部分
PHP 代码层绕过 Nginx 限制的临时方案
当 Nginx 正向代理无法快速适配多端口 HTTPS 场景时,可在业务代码中直接调用代理:
- curl 方式:明确指定
CURLOPT_PROXY为 Nginx 代理地址,并启用CURLOPT_SSL_VERIFYPEER关闭证书校验(仅测试环境) - Guzzle:构造 Client 时传入
'proxy' => 'http://ip:port',它会自动处理 CONNECT 隧道(依赖底层 cURL 支持) - 注意:PHP 层代理不解决浏览器访问问题,仅适用于后端服务间调用


















