今天来复盘一个在某客户环境中遇到的故障排查案例。现象是部分静态资源请求失败,但业务逻辑看似正常。排查过程一波三折,最终发现是 Referer 请求头惹的祸。希望这个排查思路能对大家有所帮助。
故障回顾
近期,客户反馈在浏览器访问业务网站时,刷新页面后会出现空白页面。页面上没有显示任何访问失败或服务器错误的提示信息。客户确认实际业务服务器运行正常,但通过浏览器就是无法正常加载页面。
这里涉及的网络架构是:客户通过办公网访问代理服务器对外暴露的端口,再由代理服务器转发请求至内网的实际业务服务器。一个典型的网络访问模型。
那么问题来了:为什么浏览器访问代理服务器暴露的业务网站,会显示内容为空呢?带着这个疑问,我们开始详细排查。
问题排查
第一步:浏览器端现象分析
我们再次通过浏览器访问业务网站,并在开发者工具中打开 Disable cache 选项。此时,我们发现业务的用户登录表单框架(HTML)已经显示出来了。
接着,我们观察网络面板。除了第一个加载登录页本身的 HTTP GET /sso/login 请求有正常的响应内容外,后续对 js、css、png 等静态资源的请求,其响应头中的 Content-Length 字段值都为 0。
这是一个非常关键的线索。响应头中 Content-Length 为 0,意味着服务器没有返回任何响应体(Body)内容。这表明请求本身可能是成功的(比如状态码200),但服务器故意或意外地没有返回数据。
第二步:网络抓包定位问题范围
为了确定问题发生在哪个环节,我们在代理服务器上使用 tcpdump 命令进行抓包。
抓包后,使用 Wireshark 工具对数据包进行分析。分析结果显示,代理服务器在向实际业务服务器请求 CSS、JS 等静态资源时,从业务服务器收到的响应体就已经是空的了。
这说明问题出在“代理服务器”到“实际业务服务器”这个阶段。而“办公网”到“代理服务器”显示为空,只是因为代理服务器没有收到业务服务器的响应内容,这是一个正常的结果。
一个重要的思考方向:现在故障范围已经缩小。那么我们需要思考,为什么业务服务器唯独在对 CSS、JS 等静态资源的请求响应时返回空内容呢?
第三步:手动模拟请求,发现突破口
为了排除浏览器和代理服务器配置的复杂性,我们在代理服务器上,尝试使用 curl 命令手动向业务服务器请求同一个 CSS 资源。
curl -I -interface eth1 -http0.9 http://172.18.248.252:10012/sso/themes/custom/css/login.css
执行结果如下(截取关键响应头):
HTTP/1.1 200 OK
Server: nginx/1.23.3
Date: Fri, 12 Sep 2025 03:56:47 GMT
Content-Type: text/css; charset=UTF-8
Content-Length: 2761 # 注意!这里返回了实际的内容长度
Cache-Control: no-cache
...
使用 curl 命令手动请求时,业务服务器竟然正常返回了资源内容,并且 Content-Length 是正确的非零值!
排查到这里,我们已经基本定位到了问题。手动 curl 命令与浏览器访问的唯一区别,就在于 HTTP 请求头不一致。
第四步:对比请求头,锁定元凶
我们将浏览器发出的请求头和 curl 命令发出的请求头进行详细对比。很快发现,浏览器在加载完登录页面后,后续请求静态资源(如CSS、JS)时,会自动在请求头中加入 Referer 字段。而我们的 curl 命令默认不包含此字段。
浏览器请求头示例:
GET /sso/themes/custom/js/encode64.js HTTP/1.1
Host: 172.18.248.252:10032
...
Referer: http://172.18.248.252:10032/sso/login # 关键区别
Accept-Encoding: gzip, deflate
...
第五步:验证并解决问题
既然怀疑是 Referer 头导致的问题,我们就在代理服务器层面尝试处理。配置代理规则,在转发请求到业务服务器之前,强制移除或修改请求中的 Referer 头信息。
配置生效后,再次通过浏览器访问业务系统,所有静态资源加载成功,页面显示完全正常!
总结与原理
Referer(是的,标准里就是拼写错误的 “Referer”,非 “Referrer”)是 HTTP 请求头的一部分。当浏览器向 Web 服务器发送请求时,如果当前请求是由另一个页面链接触发的,浏览器通常会自动在请求头中加入 Referer 字段,用于告知服务器请求的来源页面地址。
为什么有 Referer 头,服务器就返回空响应?
答案是:业务服务器配置了 Referer 校验策略(黑名单/白名单)。
工作原理:
服务器端会检查每个请求的 Referer 字段。
- 白名单模式:只允许来自指定域名或页面的请求访问资源。例如,只允许来自
https://example.com/home 的请求加载本站的CSS。
- 黑名单模式:拒绝来自指定来源的请求。例如,拒绝所有空
Referer 或来自未知域名的请求。
在本案例中,业务服务器很可能配置了白名单,只允许特定的 Referer(可能为空,或为某个内部地址)访问静态资源。当请求通过代理服务器转发时,Referer 值变成了代理服务器暴露给客户端的公网地址(如 http://代理IP:端口/sso/login),这个地址不在业务服务器的白名单内,因此请求被拒绝。服务器返回了 200 状态码(可能是为了隐蔽行为),但将响应体置空(Content-Length: 0)。
这种做法常被用于防止盗链(Hotlinking),即其他网站直接引用本站的图片、视频等资源,从而节省自身的带宽和服务器资源。但在复杂的代理或微服务网络/系统中,如果配置不当,就容易引发本文所述的“内部请求被误拦截”的问题。
这次故障排查经历再次提醒我们,在遇到网络请求异常时,对比分析请求的完整细节(特别是请求头)至关重要。一个小小的 Referer 头,就足以让整个页面“消失”。如果你在运维工作中也遇到过类似棘手的网络问题,欢迎到 云栈社区 的交流板块分享你的经验和解决方案。