NGINX多server块相互影响问题:为何80端口server块无proxy_pass时443端口反向代理失效?
看起来你遇到的问题本质是NGINX的server块匹配规则和默认server选择逻辑在搞鬼,再加上你可能对server_name _的作用有误解,导致请求被错误路由了。咱们一步步拆解:
1. 先澄清一个误区:server_name _不是“万能默认”
很多人以为server_name _会匹配所有未匹配的请求,但NGINX官方文档明确说明:_只是一个普通的server_name值,它不会作为默认server使用。真正决定哪个server是默认的,是listen指令后的default_server参数——如果没有显式指定,NGINX会把第一个匹配对应端口的server块设为该端口的默认server。
2. 你的配置为什么会“互相影响”?
我们对比你两次配置的差异:
正常工作的配置(80端口server块有proxy_pass)
# 443端口server块(处理HTTPS请求) server { listen 443 ssl http2; listen [::]:443 ssl http2; include snippets/self-signed.conf; include snippets/ssl-params.conf; server_name secondave.net www.secondave.net; location / { proxy_pass http://localhost:3000; } } # 80端口server块(处理HTTP请求) server { listen 80; listen [::]:80; server_name _; location / { proxy_pass http://localhost:3000; } }
这时,对于HTTPS请求(443端口),第一个匹配的server块就是上面的443 server,它的server_name正好匹配你的域名secondave.net,所以请求被正确代理到3000端口。
失效的配置(80端口server块无proxy_pass)
# 443端口server块(代码不变) server { listen 443 ssl http2; listen [::]:443 ssl http2; include snippets/self-signed.conf; include snippets/ssl-params.conf; server_name secondave.net www.secondave.net; location / { proxy_pass http://localhost:3000; } } # 80端口server块(去掉了location和proxy_pass) server { listen 80; listen [::]:80; server_name _; }
这时你访问https://secondave.net却返回NGINX默认页面,核心原因大概率是下面两种情况之一:
- 你的
http块下还有其他更早出现的、监听443端口的server块(比如默认安装的default.conf里的配置),当你的自定义443 server块不是第一个时,NGINX会把更早的那个设为默认server。如果请求的Host头匹配失败(比如自签名证书导致SNI不生效),就会落到这个默认server,返回默认页面。 - SELinux限制:Amazon Linux 2023默认开启SELinux,当你修改80端口的server块后,SELinux的上下文规则可能阻止了NGINX向3000端口发起代理请求。你可以检查错误日志确认:
cat /var/log/nginx/error.log | grep "SELinux"
3. 你后续测试的404问题是怎么回事?
你后来把80端口的server块改成:
server { listen 80; listen [::]:80; server_name secondave.net www.secondave.net; return 404; }
这时访问https://secondave.net返回404,这说明你的HTTPS请求居然被这个80端口的server块处理了——这显然不符合逻辑,唯一的解释是:你可能误修改了443端口的server块,比如不小心删掉了它的listen 443指令,或者把两个server块的内容合并到了一起,导致这个80端口的server块同时处理了443的请求,从而返回404。
4. 正确的解决方法
方法一:显式指定默认server
给你的443端口server块加上default_server参数,确保它是443端口的默认处理者:
server { listen 443 ssl http2 default_server; listen [::]:443 ssl http2 default_server; include snippets/self-signed.conf; include snippets/ssl-params.conf; server_name secondave.net www.secondave.net; location / { proxy_pass http://localhost:3000; } }
同时,给80端口的server块也加上default_server,并合理处理HTTP请求(比如跳转到HTTPS,不要空着):
server { listen 80 default_server; listen [::]:80 default_server; server_name _; # 推荐把HTTP请求永久跳转到HTTPS return 301 https://$host$request_uri; }
方法二:排查SELinux规则
如果是SELinux的问题,你可以临时关闭SELinux测试(不推荐长期关闭):
sudo setenforce 0
如果测试正常,再添加永久规则允许NGINX代理:
sudo setsebool -P httpd_can_network_connect on
方法三:清理多余的server块
检查/etc/nginx/conf.d/目录下的其他配置文件,看有没有更早加载的、监听443端口的server块,把它们注释掉或者修改,确保你的自定义443 server块是第一个被加载的。
备注:内容来源于stack exchange,提问作者Jackie

