Nginx反向代理下静态文件路由配置问题求助
大家好,我最近在折腾Nginx反向代理的配置,碰到个静态文件路由的问题,卡了好一会儿,想请各位大佬支支招~
现状描述
我现在把Nginx作为服务跑在80端口,接收客户端的所有请求,同时有几个Docker容器各自暴露不同端口跑着独立的应用。目前我的Nginx配置大致是这样的:
server { listen 80; listen [::]:80; server_name server_name; location /app1{ proxy_pass http://ip:3006/; } location /app2{ proxy_pass http://ip:3007/; } location /app3{ proxy_pass http://ip:3009/; } }
每个应用本身的静态文件(css、js、媒体文件这些),直接用容器端口访问时都是正常的,但通过Nginx的/app1、/app2这类路径访问就出问题了。
需求
我想要实现的是:用户访问http://ip/app1/media/dummy.wav这类路径时,Nginx能自动把请求路由到对应容器的正确资源上。
遇到的问题
现在的问题是,当用户通过Nginx访问应用时,页面里的静态资源加载失败了。我查了浏览器的网络请求发现,应用里的静态资源引用都是根路径开头的(比如/css/style.css),但通过Nginx代理后,浏览器会直接请求http://ip/css/style.css,而不是http://ip/app1/css/style.css,导致Nginx找不到这些资源,返回404。
可行解决方案
后来查了一些资料,整理出几个可行的解决办法,分享给大家:
1. 修改Nginx配置,修正请求路径并传递上下文信息(推荐)
这个方法不需要改应用代码,通过Nginx的rewrite和请求头来处理。修改后的配置如下:
server { listen 80; listen [::]:80; server_name server_name; location /app1/ { # 把请求路径中的/app1前缀去掉,转发给后端容器 rewrite ^/app1/(.*)$ /$1 break; proxy_pass http://ip:3006/; # 告诉后端应用当前的上下文路径,部分框架会用这个生成正确的资源链接 proxy_set_header X-Forwarded-Prefix /app1; # 传递必要的代理头,确保后端能获取客户端真实信息 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location /app2/ { rewrite ^/app2/(.*)$ /$1 break; proxy_pass http://ip:3007/; proxy_set_header X-Forwarded-Prefix /app2; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location /app3/ { rewrite ^/app3/(.*)$ /$1 break; proxy_pass http://ip:3009/; proxy_set_header X-Forwarded-Prefix /app3; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }
这里有几个关键点:
- 把
location /app1改成location /app1/(末尾加斜杠),避免误匹配类似/app12这类路径 - 通过
rewrite规则提取/app1后的路径部分,转发给后端容器 - 添加
X-Forwarded-Prefix头,让后端框架知道当前的上下文路径,生成带前缀的资源链接
2. 修改应用内的静态资源引用为相对路径
如果能修改应用代码的话,可以把所有静态资源的引用改成相对路径。比如把<link href="/css/style.css">改成<link href="./css/style.css">或者<link href="css/style.css">,这样浏览器在/app1路径下会自动请求/app1/css/style.css,Nginx就能正确转发了。不过这个方法需要修改应用代码,适合小项目或者自己能控制代码的场景。
3. 用Nginx的sub_filter替换响应中的资源路径
如果没法修改应用代码,还可以让Nginx在返回给客户端的HTML里,自动替换根路径的资源链接为带前缀的路径。配置示例:
location /app1/ { proxy_pass http://ip:3006/; proxy_set_header Host $host; # 替换HTML中的根路径资源链接为带/app1前缀的路径 sub_filter '/css/' '/app1/css/'; sub_filter '/js/' '/app1/js/'; sub_filter '/media/' '/app1/media/'; # 开启多次替换,确保所有匹配的路径都被处理 sub_filter_once off; }
这个方法的缺点是需要逐个配置要替换的资源路径前缀,如果应用里的资源路径比较多,配置会比较繁琐,而且动态生成的路径可能会漏替换。
验证方法
改完配置后,执行nginx -s reload重启Nginx,然后访问http://ip/app1,打开浏览器开发者工具(F12)查看网络请求,确认静态资源的请求路径是/app1/css/xxx.css这类带前缀的路径,并且返回200状态码,就说明配置生效啦~
备注:内容来源于stack exchange,提问作者weird




