NGINX配置HTTPS反向代理本地HTTP服务时偶发403问题
问题根因
这个问题是Nginx监听配置不明确+MacOS系统默认IPv6 socket配置共同导致的,核心逻辑如下:
- MacOS默认开启
net.inet6.ip6.v6only=1参数,IPv6 socket不会自动兼容接收IPv4连接,两个协议栈的端口需要独立绑定 - 你配置中用来做HTTP跳HTTPS的第一个server块没有显式指定
listen 80监听规则,Nginx会自动给未配置listen的server分配监听端口:因为你配置中存在443 SSL监听,且你只给HTTPS的server写了listen 443 ssl,在v6only开启的环境下,这条规则默认只会绑定IPv6的[::]:443,不会自动绑定IPv4的0.0.0.0:443 - Nginx发现IPv4的443端口未被绑定,就自动把第一个没有listen配置的server绑定到了
0.0.0.0:443上。这个server块没有配置proxy_pass规则,没有配置静态资源根目录,请求到达后会由Nginx直接尝试读取默认静态目录下的文件,找不到对应资源/无目录权限时就会直接返回403,不会转发给后端服务,也不会在error.log打印错误 - Firefox访问本地域名时会同时尝试建立IPv4(127.0.0.1)和IPv6(::1)的连接,哪个连接先建立成功就用哪个:走IPv6连接时请求命中配置了代理的HTTPS server,正常转发返回200;走IPv4连接时请求命中被自动绑定到443的跳转server,直接返回403,因此会出现交替返回200/403的现象。
修复方案
按如下方式修改Nginx配置即可:
- 给HTTP跳转的server块显式添加80端口的监听规则,同时覆盖IPv4和IPv6:
server { listen 80; listen [::]:80; server_name my-service.foo.com; rewrite ^(.*) https://my-service.foo.com$1 permanent; }
- 给HTTPS的server块显式添加IPv4和IPv6的443端口监听,避免端口被自动分配给其他server,同时把proxy_pass地址改为明确的IPv4地址,避免localhost解析带来的额外问题:
server { listen 443 ssl; listen [::]:443 ssl; ssl_certificate /opt/local/etc/nginx/myservice.crt; ssl_certificate_key /opt/local/etc/nginx/myservice.key; ssl_ciphers HIGH:!aNULL:!MD5; server_name my-service.foo.com; proxy_set_header Host $host; location / { proxy_pass http://127.0.0.1:7000; } }
补充说明:把
localhost改为127.0.0.1是为了避免系统把localhost解析为IPv6的::1地址时,因后端服务未监听IPv6端口出现502错误。
- 配置修改完成后,执行
nginx -t检查配置语法无报错,再执行nginx -s reload重载配置,所有请求就会正常转发到后端服务,不会再出现偶发403的问题。
内容的提问来源于stack exchange,提问作者Jie Huang
相关产品推荐
相关产品推荐

