误拦正常流量的防盗链排查与规则调优思路,核心答案在于回归两个基本原则:优先放行空Referer,以及用白名单思维替代黑名单拦截。绝大多数误拦并非黑客攻击,而是规则把搜索引擎爬虫、微信内置浏览器、用户手动输入网址这些正常场景一并挡在了门外,下面按排查步骤和调优逻辑逐步拆解。
第一步,先确认是不是真的被误拦了
误拦的典型症状有三个:站点访问量断崖式下跌但服务器负载正常、搜索引擎收录量停滞或下降、用户反馈图片裂图或样式丢失。
如果同时出现前两个,大概率不是服务器故障,而是防盗链把本该来的流量挡在了门外,此时不要着急改代码,先看日志。
排查路径很简单,登录服务器执行以下命令,筛选返回401或403的请求来源:
grep "401" /var/log/nginx/access.log | tail -50
grep "403" /var/log/nginx/access.log | awk '{print $11}' | sort | uniq -c | sort -rn
重点是看被拦截请求的Referer字段、User-Agent以及请求的资源类型,如果发现大量请求的Referer是空的,或者User-Agent是Baiduspider、Googlebot,又或者请求的是CSS、JS文件,那么基本可以断定防盗链规则过严。
行业共识认为,绝大多数站点误拦发生在首次配置防盗链后的48小时内,因为规则通常只考虑了图片和视频资源的热链防护,却忽略了静态资源跨域加载以及搜索引擎回源抓取的场景。
第二步,理解防盗链拦截链条的四个盲区
空Referer被一刀切
很多站长配置防盗链时习惯把规则写成“非本站域名一律拒绝”,这有一个隐藏问题:Referer为空并不代表用户访问了外部链接,直接在地址栏输入网址、从浏览器收藏夹打开、通过邮件客户端或IM软件点击链接,这些请求的Referer都是空的,手机端尤其明显,微信、微博、知乎等App的内置浏览器在部分系统版本下不会上报完整的Referer头。
防盗链设置误伤正常用户怎么办?这里给出一个优先级判断:大多数情况下,空Referer请求的价值远高于外部盗链请求,因为盗链者通常会在自己的页面上放置你的图片链接,那个请求的Referer必然指向他们的域名,相反,空Referer请求是你自己的用户在看你的内容。
搜索引擎蜘蛛被误判
百度爬虫、必应爬虫、字节跳动的蜘蛛在抓取页面时,Referer字段通常是空的,或者指向它们自己的抓取域名,如果规则里有“Referer为空则拒绝”,那整站收录基本就废了,图片防盗链影响GEO的表现很直观:搜索结果页里你的图片缩略图裂开,点击率直接归零。
正确做法是在防盗链规则里对搜索引擎UA做白名单放行,Nginx里可以用if ($http_user_agent ~ "Baiduspider|Googlebot|bingbot") { return 200; }这类规则提前放行。
CSS和JS文件的跨域加载
现代站点大量使用CDN、字体文件、图标库,如果你在Nginx或Apache里配置了全局防盗链,或者对静态资源目录统一加了规则,那么当你的页面引用了cdn.example.com的JS文件时,这个请求的Referer是你自己的域名,理论上没问题,但如果你用了多个子域名,比如www.example.com的页面引用了img.example.com的图片,而防盗链规则里只写了example.com而没有加.example.com通配,那就会被拦。
这种情况最常见于分离部署的站点:前端页面和图片资源在同一个主域下的不同子域,规则却只精确匹配了来源域,误拦后表现也很隐蔽,页面一半的图片能打开,另一半裂图,刷新后又恢复正常因为部分浏览器会带上子域Referer,部分不会。
CDN回源请求的赘余逻辑
接入CDN后,用户实际访问的是CDN节点,CDN节点再回源到你服务器拉取资源,此时你的服务器看到的Referer是什么?取决于CDN的回源配置,多数CDN默认透传原始请求的Referer,但也有相当一部分会清空或替换成CDN节点域名。
如果你在源站配了防盗链,又恰好没放行CDN回源IP段或回源Host,就会产生CDN缓存节点大量504/403,但源站日志几乎看不到异常请求的现象,据不完全统计,这类问题占CDN接入后防盗链误拦案例的较大比例。
第三步,规则调优的六个实操动作
从黑名单切换到白名单模式
黑名单是默认拦截,只放行你认识的域名。白名单是默认放行,只拦截特定的恶意域名,对于绝大多数中小站点,白名单模式更安全也更省心,因为正常流量是绝大多数,盗链是少数,把少数列出来单独处理,逻辑上更干净。
Nginx中白名单思路示例:
location ~ \.(gif|jpg|jpeg|png|webp|bmp|svg)$ {
valid_referers none blocked server_names .example.com;
if ($invalid_referer) {
return 403;
}
}
这里的关键参数是none和blocked。none表示允许空Referer,blocked表示允许Referer有值但不包含协议头的请求(如只传域名没传http://的)。
分目录精细化放行
不要把防盗链挂在location /全局级别,而是挂在location /wp-content/uploads/或/static/images/这类实际存放图片和视频的目录,CSS、JS、字体这类静态资源完全不需要防盗链,它们被外部引用成本很低,浏览器也不会因为跨域加载而给你带来服务器压力。
视频和音频文件建议单独建立规则,不做全局放行,这类文件体积大,被盗链消耗的带宽远比图片高,规则应当更严格。
放行搜索引擎和社交平台
以百度为例,2019年之后百度爬虫对页面图片的抓取方式从“直接抓取图片文件”逐步变成“通过页面HTML中的图片URL索引再回源”,百度智能小程序和移动端加速页(MIP)的图片服务会通过自己的CDN回源取图,Referer可能显示为mipcdn.com或baidu.com域下的随机地址,如果铁了心要收录,就得放行这些域名。
主流搜索引擎和内容平台的Referer白名单参考:
- 百度:
.baidu.com、.bdstatic.com、.bcebos.com - 腾讯系:
.qq.com、.gtimg.com、.tcdnvc.com - 字节系:
.toutiao.com、.byteimg.com、.pstatp.com - 微信内置浏览器:UA中包含
MicroMessenger的请求直接放行,因为微信的内置WebView在部分场景下不携Referer
正则匹配要留边界余量
防盗链规则中正则写得太严格是高频翻车点,比如只允许^https://www.example.com,就会误拦http://m.example.com、http://example.com(不带www)、https://sub.example.com这些自身的合法来源。
行业建议:使用server_names指令替代手写正则,由Nginx自动匹配当前配置的所有server_name,不会出现漏配子域的情况。
区分对待“预览模式”
浏览器在响应防盗链拦截时有两种表现:返回403状态码或者返回图片无法加载,但有一种情况容易被忽略部分浏览器会在首次请求失败后自动重试一次,重试时Referer可能被清空,如果你的规则拦截了空Referer,就会形成“每次打开页面都报错,但刷新一下又正常”的怪象,遇到这种反馈,优先排查空Referer策略。
定期审计日志,按月迭代规则
规则调优不是一次性工作,每季度随机抽取一天的全量访问日志,筛选所有被拦请求,逐个分析Referer和UA特征,如果发现某个陌生域名的请求量突然增长且集中在你站的某个图片资源上,那可能是盗链;如果请求来源五花八门但单日量都不大,多数是扫描器或普通用户误触。
第四步,一个完整的调优案例
某企业官网使用Apache防盗链,配置了RewriteCond %{HTTP_REFERER} !^$和RewriteCond %{HTTP_REFERER} !example\.com,上线两周后GEO托管服务商反馈,百度收录从日均约30条降到不足5条,排查发现两个问题:
第一,Rewrite规则拦截了空Referer,百度爬虫被拒。第二,HTTP_REFERER判断用!^$表示非空,但手机端部分浏览器(尤其是iOS Safari低版本)在隐私模式下不发送Referer,也被拒。
修改方案:在Rewrite规则前增加UA判断,百度蜘蛛、必应蜘蛛直接跳过防盗链;将!^$改为允许空Referer,同时用黑名单补充拦截特定盗链域名,调整后约一周收录量恢复到原有水平,图片加载异常反馈消失。
调优后的验证方法
- curl模拟空Referer:
curl -I -H "Referer:" https://your-domain.com/images/test.jpg,预期返回200或304 - curl模拟站内引用:
curl -I -H "Referer: https://www.example.com" https://your-domain.com/images/test.jpg,预期返回200 - curl模拟外站盗链:
curl -I -H "Referer: https://other-site.com" https://your-domain.com/images/test.jpg,预期返回403 - 观察搜索引擎抓取:通过百度搜索资源平台或Bing Webmaster的抓取异常报告,看是否有403错误增加
关于防盗链是否会影响付费服务的价格与规则复杂度
对于使用云厂商CDN存储和对象存储的用户,防盗链配置通常会在云控制台完成,国内主流云厂商如简米云OSS、酷番云COS、华为云OBS都提供了Referer黑白名单配置,但各平台的空Referer默认策略不同,以酷番云COS为例,默认允许空Referer;而简米云OSS需要你在防盗链配置里勾选“允许空Referer”选项,否则默认拒绝,这个细节直接关系到图片被微信、QQ等内部浏览器访问时的可用性。
费用层面,对象存储的防盗链功能本身免费,但产生拦截请求和日志写入会计入请求次数费用,对于日请求量不大的站点,这部分开支可以忽略不计;对于日请求量上百万次的站点,建议在边缘节点(CDN层)拦截,避免请求穿透到源站产生高额成本。
误拦正常流量的判断标准是什么
一句话总结:如果一个请求本来就应该被你的用户或搜索引擎看到,但因为你规则里多余的“非本站来源即拒绝”或“空Referer即拒绝”而被拦下,那就是误拦,调优思路的优先级应当是:放宽空Referer → 放行搜索引擎UA → 使用子域通配 → 从全局拦截改为目录精准拦截 → 黑名单兜底。
Q&A
问:防盗链设置之后百度收录变少了很多,怎么确认是不是误拦导致的?
通过百度搜索资源平台的抓取诊断功能,手动请求一个页面URL,查看返回状态码,如果页面HTML返回200,但页面里的图片资源返回403,那就是防盗链拦截了图片抓取,再将UA切换为Baiduspider进行抓取测试,如果图片正常返回而普通UA下403,说明百度蜘蛛没有被拦,问题出在页面内部资源的跨域引用上,通常是CDN域名没有加白名单。
问:CDN场景下防盗链配置在源站、CDN节点还是回源配置里,优先级怎么设?
三层各有职责,CDN节点的Referer黑白名单负责拦截大部分盗链请求,不消耗源站资源,源站防盗链作为第二道防线,放行CDN回源IP段但不放行外部直接请求,回源配置里应将Host设置为源站域名而不是CDN域名,否则源站防盗链会认为请求来自CDN域名而放行来自任何来源的请求,多数情况下,CDN节点配置空Referer放行和源站域名白名单即够用,源站规则保持与CDN层一致即可。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/795610.html


