Nginx反向代理多应用时资源加载失败的配置求助
嘿,我碰到过一模一样的问题!这事儿根源其实很清楚:你部署在5300端口的/app2应用,它返回的HTML里引用资源的路径是绝对路径(比如/assets/css/xxx),当用户访问http://127.0.0.1:8080/app2/时,浏览器会直接请求http://127.0.0.1:8080/assets/css/xxx——而Nginx里的location /规则优先级匹配到这个请求,就把它转发到5200端口的应用了,自然就加载失败。/api路径的问题也是同一个道理。
下面给你几个实用的解决办法,你可以根据自己的情况选:
方法一:修改应用的资源引用为相对路径(最推荐)
如果能改/app2和/api对应应用的代码,直接把所有绝对路径的资源引用(比如/assets/xxx)改成相对路径(比如./assets/xxx或者assets/xxx)。这样浏览器会基于当前页面的URL(/app2/)去请求/app2/assets/xxx,Nginx的location /app2/规则就能匹配到,自动转发到5300端口,资源就能正常加载了。
这种方法最干净,后续维护也省心,没有Nginx配置上的额外开销。
方法二:用Nginx的sub_filter修改响应中的资源路径(无需改应用代码)
如果没法修改应用代码,可以让Nginx在转发/app2的响应时,自动把HTML里的绝对路径资源替换成带/app2前缀的路径。具体配置如下:
events {} http { server { listen 8080; server_name 127.0.0.1; rewrite_log on; error_log /var/log/nginx/error.log notice; location / { proxy_pass http://127.0.0.1:5200; proxy_set_header Host 127.0.0.1:5200; } location /api { proxy_pass http://127.0.0.1:8000; proxy_set_header Host 127.0.0.1:8000; # 同样处理/api的资源路径 sub_filter '/assets/' '/api/assets/'; sub_filter_once off; } location = /app2 { return 302 /app2/; } location /app2/ { proxy_pass http://127.0.0.1:5300/; proxy_set_header Host 127.0.0.1:5300; # 替换HTML中所有/assets/为/app2/assets/ sub_filter '/assets/' '/app2/assets/'; sub_filter_once off; # 确保替换所有匹配的资源路径 } # 新增处理/app2/assets的路由,转发到5300的对应路径 location /app2/assets/ { proxy_pass http://127.0.0.1:5300/assets/; proxy_set_header Host 127.0.0.1:5300; } # 新增处理/api/assets的路由,转发到8000的对应路径 location /api/assets/ { proxy_pass http://127.0.0.1:8000/assets/; proxy_set_header Host 127.0.0.1:8000; } } }
注意:这个方法需要你的Nginx编译时包含了ngx_http_sub_module模块,大部分主流发行版的预编译Nginx都带这个模块,如果没有的话需要重新编译Nginx加上这个模块。
方法三:根据Referer路由资源请求(适合不能改应用也不想用sub_filter的场景)
另一种思路是,通过浏览器发送的Referer请求头,判断资源请求来自哪个应用的页面,然后转发到对应的上游端口。配置示例:
events {} http { # 定义一个map,根据Referer判断资源要转发到哪个上游 map $http_referer $asset_upstream { ~*/app2/ http://127.0.0.1:5300; ~*/api/ http://127.0.0.1:8000; default http://127.0.0.1:5200; } server { listen 8080; server_name 127.0.0.1; rewrite_log on; error_log /var/log/nginx/error.log notice; location / { proxy_pass http://127.0.0.1:5200; proxy_set_header Host 127.0.0.1:5200; } location /api { proxy_pass http://127.0.0.1:8000; proxy_set_header Host 127.0.0.1:8000; } location = /app2 { return 302 /app2/; } location /app2/ { proxy_pass http://127.0.0.1:5300/; proxy_set_header Host 127.0.0.1:5300; } # 处理所有/assets开头的资源请求,根据map结果转发 location /assets { proxy_pass $asset_upstream; proxy_set_header Host $host; } } }
不过这个方法有个小缺点:如果浏览器没有发送Referer头(比如某些隐私模式下),资源会默认转发到5200端口,还是会出错,所以优先级不如前两种方法。
你可以先试试方法一,要是改应用代码不方便,就用方法二,亲测有效!
备注:内容来源于stack exchange,提问作者Asmaa

