Nginx 反向代理时如何正确处理相对 URL?
相对 URL 错乱的处理:proxy_pass 路径对称、proxy_redirect 修正重定向头、sub_filter 改写页面内链接、Cookie 路径用 proxy_cookie_path。首选让后端感知 base path。本文给完整方案。
**处理四件套:①proxy_pass 路径映射写对称(前缀替换规则想明白);②proxy_redirect 修正后端 302 的 Location 头;③sub_filter 改写 HTML 里的链接与资源路径;④proxy_cookie_path 修 Cookie 作用域。最省心的根治:让后端应用配置 base path(如 /app1),前后端路径天然一致,不用改写任何内容。
路径映射:两种写法的差别
location /app1/ {
proxy_pass http://internal:8080/; # /app1/x → /x(前缀被替换)
}
location /app1/ {
proxy_pass http://internal:8080; # /app1/x → /app1/x(原样转发)
}
末尾有无 / 决定前缀替换还是原样转发。后端不感知 /app1 前缀时用第一种,同时处理三类"出界":
① 重定向头:proxy_redirect
location /app1/ {
proxy_pass http://internal:8080/;
proxy_redirect http://internal:8080/ /app1/;
# 或宽松写法
proxy_redirect ~^https?://+/(.*)$ /app1/$1;
}
② 页面内容:sub_filter
location /app1/ {
proxy_pass http://internal:8080/;
sub_filter 'href="/' 'href="/app1/';
sub_filter 'src="/' 'src="/app1/';
sub_filter_once off;
sub_filter_types text/html text/css application/javascript;
}
注意 sub_filter 默认只处理 text/html;CSS/JS 里的路径要扩 sub_filter_types。gzip 响应需先 proxy_set_header Accept-Encoding ""; 让后端不压缩,才能改文本。
③ Cookie 路径:proxy_cookie_path
proxy_cookie_path / /app1/;
后端种下的 Cookie 路径是 /,不改写会泄漏到整个域(或前端路由下 Cookie 不带)。
根治方案
主流框架都支持配置 base path(Django FORCE_SCRIPT_NAME、Spring server.servlet.context-path、前端构建的 base/publicPath)。让后端"知道自己挂在 /app1 下",生成的一切链接自动带前缀——以上改写全部不需要。
观测云对照
改写遗漏用 404 分布定位。 采集 Nginx 访问日志后,404 请求的路径分布图直接指出哪些资源路径没改写到;修复后 404 清零可验证。
常见问题(FAQ)
Q:sub_filter 没生效?
A:检查响应 Content-Type 是否在 sub_filter_types 里、响应是否 gzip 压缩(压缩的改不了,先禁后端压缩)。
Q:AJAX 请求的 URL 是 JS 变量拼的,sub_filter 改不到?
A:对——JS 动态拼的 URL 静态改写无能为力,这正是推荐后端感知 base path 的原因。
Q:proxy_redirect default 是什么行为?
A:默认(不配置时)按 proxy_pass 的映射自动处理 Location 头,多数简单场景已够;自定义域名/端口时才需显式写。