反向代理时脚本相对路径解析异常问题排查与修复
./script.js 被解析为 /script.js 的问题与修复 问题场景
我用Go搭建了反向代理,从/hello路径代理后端Go服务器;后端服务器从根路径/提供HTML页面,页面中script标签使用相对路径./script.js。但访问http://127.0.0.1:4321/hello时,浏览器将./script.js解析为/script.js,导致请求走根路径而非代理的/hello路径,无法正确获取资源。
疑问:按道理,浏览器在/hello路径下,./script.js应该解析成/hello/script.js才对?
问题根源
浏览器解析相对路径的规则是基于当前页面URL的路径层级判定:
- 如果URL路径末尾带斜杠(比如
/hello/),浏览器会认为这是一个目录,相对路径./script.js会解析为/hello/script.js - 如果URL路径末尾不带斜杠(比如
/hello),浏览器会把hello当作一个文件,相对路径./script.js会解析为和该文件同级的路径,也就是/script.js
你的场景中,访问的是无末尾斜杠的/hello,浏览器将hello视为文件,因此相对路径直接指向根目录下的script.js。
修复方案
方案1:代理自动重定向到带末尾斜杠的路径
在反向代理的/hello处理函数中,检查请求路径是否带末尾斜杠,无斜杠则重定向到/hello/,同时调整路径替换逻辑兼容新路径:
http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) { // 无末尾斜杠时重定向 if r.URL.Path == "/hello" { http.Redirect(w, r, "/hello/", http.StatusPermanentRedirect) return } log.Printf("hello request URL: %s\n", r.URL) proxy.ServeHTTP(w, r) log.Printf("hello response: %+v\n", w) }) // 调整Director中的路径替换逻辑 proxy.Director = func(req *http.Request) { req.URL.Scheme = targetURL.Scheme req.URL.Host = targetURL.Host req.Host = req.URL.Host req.Header.Del("If-None-Match") req.Header.Del("If-Modified-Since") req.Header.Set("Accept-Encoding", "") // 兼容/hello和/hello/两种路径 if strings.HasPrefix(req.URL.Path, "/hello/") { req.URL.Path = strings.Replace(req.URL.Path, "/hello/", "/", 1) } else if req.URL.Path == "/hello" { req.URL.Path = "/" } fmt.Printf("replaced: %s\n", req.URL.Path) }
用户访问/hello时会自动跳转到/hello/,浏览器会正确解析相对路径。
方案2:在HTML中添加<base>标签
在页面<head>中添加<base>标签,指定所有相对路径的基准URL:
<head> <meta charset="UTF-8"> <title>Swagger UI</title> <base href="/hello/"> </head>
无论页面URL是否带末尾斜杠,浏览器都会基于/hello/解析所有相对路径,./script.js会直接解析为/hello/script.js。
方案3:修改script路径为绝对路径
直接将script的src改为代理路径下的绝对路径:
<script src="/hello/script.js" charset="UTF-8"> </script>
这种方法简单直接,但如果代理路径(如/hello)后续需要修改,需同步调整HTML路径,灵活性较差。
方案4:代理拦截根路径静态资源请求
如果不想修改前端和访问路径,可在代理中拦截根路径的静态资源请求,转发到后端服务器:
// 单独处理script.js请求 http.HandleFunc("/script.js", func(w http.ResponseWriter, r *http.Request) { r.URL.Path = "/script.js" proxy.ServeHTTP(w, r) })
此方法需为每个静态资源单独配置,适合资源数量少的场景。
内容的提问来源于stack exchange,提问作者oyevtushe

