NGINX反向代理location重写异常:后端重定向未按预期改写
问题
我正在配置NGINX反向代理,允许客户端通过代理的特定URL(如https://ip/service1、https://ip/service2)访问对应后端服务,但代理的location块URL与后端服务的基础URL不一致,且无法修改后端服务的基础URL。当前使用nginx/1.18.0(Debian 11环境),配置如下:
server { listen 443 ssl http2; server_name 192.168.1.4; # ssl_certificate /etc/ssl/certs/nginx-selfsigned.crt; ssl_certificate_key /etc/ssl/private/nginx-selfsigned.key; # ssl_protocols TLSv1.3; ssl_prefer_server_ciphers on; ssl_ciphers TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:TLS_AES_128_CCM_8_SHA256:TLS_AES_128_CCM_SHA256:ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AE> ssl_ecdh_curve secp384r1; ssl_session_cache shared:SSL:10m; ssl_session_tickets off; ssl_dhparam /etc/ssl/certs/dhparam.pem; # location / { index index.html; root /var/www/; } # location /service1 { rewrite /service1/(.*) /$1 break; proxy_redirect off; proxy_ssl_protocols TLSv1.3; proxy_pass https://192.168.3.2/; proxy_set_header Host "192.168.3.2"; proxy_set_header X-Real-IP $remote_addr; } location /service2 { rewrite /service2/(.*) /$1 break; proxy_redirect off; proxy_ssl_protocols TLSv1.3; proxy_pass https://192.168.3.2:8443/; proxy_set_header Host "192.168.3.2"; proxy_set_header X-Real-IP $remote_addr; } }
location /块用于提供各服务的快捷访问页面。我理解location块中的rewrite规则会将客户端含/service1的请求改写为/加后续路径,但访问https://192.168.1.4/service1时,登录页无CSS加载,登录后被后端重定向至https://192.168.1.4/index.php,而非预期的https://192.168.1.4/service1/index.php。CSS加载异常疑似与此相关。请问我的rewrite规则是否有误?能否在客户端与后端URL不同的情况下实现正确的proxy_pass?
解决方案
你的配置存在两个核心问题:
1. Rewrite规则的匹配局限性
原rewrite /service1/(.*) /$1 break;仅匹配带有/service1/(末尾斜杠)的请求,当用户访问/service1(无末尾斜杠)时,该规则不会触发,NGINX会自动重定向到/service1/,但后端返回的静态资源(如CSS、JS)使用绝对路径(例如/css/style.css),客户端会直接请求https://192.168.1.4/css/style.css,该请求被location /处理,无法获取后端的资源,导致加载失败。
2. 后端重定向未适配代理前缀
原配置proxy_redirect off;关闭了重定向URL的修改,后端返回的重定向路径(如/index.php)会直接返回给客户端,客户端跳转时不会带上/service1前缀,因此出现跳转到根路径index.php的问题。
修改后的配置示例(以service1为例)
server { listen 443 ssl http2; server_name 192.168.1.4; # SSL配置保持不变 ssl_certificate /etc/ssl/certs/nginx-selfsigned.crt; ssl_certificate_key /etc/ssl/private/nginx-selfsigned.key; ssl_protocols TLSv1.3; ssl_prefer_server_ciphers on; ssl_ciphers TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:TLS_AES_128_CCM_8_SHA256:TLS_AES_128_CCM_SHA256:ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384; ssl_ecdh_curve secp384r1; ssl_session_cache shared:SSL:10m; ssl_session_tickets off; ssl_dhparam /etc/ssl/certs/dhparam.pem; location / { index index.html; root /var/www/; } # 处理不带末尾斜杠的请求,统一重定向到带斜杠的路径 location = /service1 { return 302 /service1/; } location /service1/ { # 替换响应内容中的绝对路径,给静态资源、表单等加上代理前缀 sub_filter 'href="/' 'href="/service1/'; sub_filter 'src="/' 'src="/service1/'; sub_filter 'action="/' 'action="/service1/'; sub_filter_once off; # 替换所有匹配项 # 修改后端返回的重定向URL,添加代理前缀 proxy_redirect https://192.168.3.2/ https://192.168.1.4/service1/; proxy_redirect / /service1/; proxy_ssl_protocols TLSv1.3; proxy_pass https://192.168.3.2/; proxy_set_header Host "192.168.3.2"; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 可选:如果后端支持,传递前缀信息 proxy_set_header X-Forwarded-Prefix /service1; } # service2的配置同理修改 location = /service2 { return 302 /service2/; } location /service2/ { sub_filter 'href="/' 'href="/service2/'; sub_filter 'src="/' 'src="/service2/'; sub_filter 'action="/' 'action="/service2/'; sub_filter_once off; proxy_redirect https://192.168.3.2:8443/ https://192.168.1.4/service2/; proxy_redirect / /service2/; proxy_ssl_protocols TLSv1.3; proxy_pass https://192.168.3.2:8443/; proxy_set_header Host "192.168.3.2"; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Prefix /service2; } }
关键修改说明
- 统一路径格式:通过
location = /service1将无斜杠的请求重定向到带斜杠的路径,确保后续所有请求都包含完整前缀。 - 替换响应内容中的绝对路径:使用
sub_filter将后端返回的/css/、/js/等绝对路径替换为/service1/css/,让客户端请求正确的代理路径资源。 - 修正重定向URL:开启
proxy_redirect并配置规则,将后端返回的根路径重定向修改为带代理前缀的路径,解决登录后跳转错误的问题。 - 移除冗余rewrite:利用NGINX原生的
location与proxy_pass匹配逻辑(proxy_pass末尾带/时,会自动将location匹配的/service1/替换为后端的/),替代原rewrite规则,更稳定可靠。
内容的提问来源于stack exchange,提问作者user19019822

