为何NGINX中使用$scheme变量的proxy_pass配置无法正常工作?
问题分析:NGINX反向代理中$scheme变量导致的访问异常
问题场景
我运行着一台NGINX服务器,用于向其他Web系统提供反向代理服务,配置如下:
server { # HTTP configuration listen 80; listen [::]:80; listen 443 ssl; listen [::]:443 ssl; server_name example.com; large_client_header_buffers 4 16k; error_log /var/log/nginx/example.com.log info; access_log /var/log/nginx/example.com.log main; ssl_certificate root/fullchain.pem; ssl_certificate_key root/privkey.pem; location / { proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_cache_bypass $http_upgrade; proxy_set_header Connection $http_connection; proxy_set_header X-Real-Port $server_port; proxy_set_header X-Real-Scheme $scheme; proxy_read_timeout 3600; resolver_timeout 60s; client_max_body_size 50M; proxy_pass $scheme://192.168.1.1/; } } server { if ($host = example.com) { return 301 https://$host$request_uri; } listen 80; listen [::]:80; server_name example.com; return 404; }
我发现使用proxy_pass $scheme://192.168.1.1/时页面无法正常工作,但改为proxy_pass http://192.168.1.1/后就能正常运行。我的目标是让该特定系统同时支持HTTP和HTTPS访问。
原因分析
- 强制重定向拦截HTTP请求:第二个server块中,所有80端口的请求都会被301重定向到HTTPS,导致第一个server块的80端口监听实际不会处理任何请求。当用户通过HTTPS访问时,
$scheme值为https,NGINX会尝试用HTTPS协议连接后端192.168.1.1。如果后端未配置HTTPS服务(无SSL证书、未开启443端口监听),就会出现连接失败,页面无法加载。 - 固定HTTP协议的兼容性:改为
proxy_pass http://192.168.1.1/后,无论前端是HTTP还是HTTPS请求,NGINX都用HTTP协议连接后端,只要后端HTTP服务正常,就能正常响应。
解决方案
要实现同时支持HTTP和HTTPS访问,可按以下两种方式调整配置:
方案一:拆分HTTP和HTTPS独立server块
分别配置处理HTTP和HTTPS的server块,按需设置proxy_pass:
# 处理HTTP请求 server { listen 80; listen [::]:80; server_name example.com; large_client_header_buffers 4 16k; error_log /var/log/nginx/example.com-http.log info; access_log /var/log/nginx/example.com-http.log main; location / { proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_cache_bypass $http_upgrade; proxy_set_header Connection $http_connection; proxy_set_header X-Real-Port $server_port; proxy_set_header X-Real-Scheme $scheme; proxy_read_timeout 3600; resolver_timeout 60s; client_max_body_size 50M; proxy_pass http://192.168.1.1/; # 后端用HTTP协议 } } # 处理HTTPS请求 server { listen 443 ssl; listen [::]:443 ssl; server_name example.com; large_client_header_buffers 4 16k; error_log /var/log/nginx/example.com-https.log info; access_log /var/log/nginx/example.com-https.log main; ssl_certificate root/fullchain.pem; ssl_certificate_key root/privkey.pem; location / { proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_cache_bypass $http_upgrade; proxy_set_header Connection $http_connection; proxy_set_header X-Real-Port $server_port; proxy_set_header X-Real-Scheme $scheme; proxy_read_timeout 3600; resolver_timeout 60s; client_max_body_size 50M; # 后端支持HTTPS可改为https://192.168.1.1/,否则保持http:// proxy_pass http://192.168.1.1/; } }
方案二:保留单个server块,移除强制重定向
若不需要将HTTP请求强制跳转HTTPS,可删除第二个server块,让第一个server块同时处理两种请求。此时需确保后端能处理对应协议请求,或统一用HTTP连接后端:
server { listen 80; listen [::]:80; listen 443 ssl; listen [::]:443 ssl; server_name example.com; large_client_header_buffers 4 16k; error_log /var/log/nginx/example.com.log info; access_log /var/log/nginx/example.com.log main; ssl_certificate root/fullchain.pem; ssl_certificate_key root/privkey.pem; location / { proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_cache_bypass $http_upgrade; proxy_set_header Connection $http_connection; proxy_set_header X-Real-Port $server_port; proxy_set_header X-Real-Scheme $scheme; proxy_read_timeout 3600; resolver_timeout 60s; client_max_body_size 50M; # 后端仅支持HTTP则写http://192.168.1.1/,支持HTTPS可使用$scheme变量 proxy_pass http://192.168.1.1/; } }
关键提示
- 若要使用
$scheme作为proxy_pass的协议部分,必须确保后端对应协议服务正常运行(比如HTTPS需后端配置SSL证书并监听443端口)。 - 强制重定向规则会阻断HTTP请求直接到达后端,若需同时支持两种访问方式,需调整重定向逻辑或拆分server块。
内容的提问来源于stack exchange,提问作者Augustus Buckman
相关产品推荐
相关产品推荐

