因配置防盗链误杀搜索引擎蜘蛛,文章分析了Nginx valid_referers配置错误导致图片收录403 Forbidden的原因。首先,搜索引擎蜘蛛抓取图片时,空Referer未被考虑,其次,图片搜索展示页Referer跨域降级导致问题,最后,User-Agent伪造和蜘蛛白名单优先级导致误判。文章建议显式允许主流搜索引擎域名,解决相关问题。
前几天看百度资源平台和 Google Search Console 的抓取异常报告,猛地发现图片类资源的抓取失败率飙升,后台全是一溜的 403 Forbidden。看索引量曲线,图片收录直接从原本的几千张腰斩。
当时第一反应是不是服务器被限流了,但翻了 access.log 发现普通的 HTML 页面抓取很正常,200 状态码跑得很稳,偏偏只要后缀是 .jpg、.png、.webp 的静态图片,搜索引擎的抓取请求一律被 Nginx 丢了 403。
顺着 Nginx 提交记录往前查,两天前刚把一套防盗链配置推上了生产环境,起因是有几个采集站天天用爬虫把图片外链贴到他们站上,白白吃掉服务器几十个 G 的 CDN 流量。
当时在 nginx.conf 里顺手写了这段常见配置:
location ~* \.(jpg|jpeg|png|gif|webp)$ {
valid_referers blocked server_names *.llbbs.cn;
if ($invalid_referer) {
return 403;
}
}
配置语法挑不出毛病,测试外链直接打不开,盗链确实被挡住了,但搜索引擎蜘蛛的抓取也一并被掐死了。
根因一:搜索引擎蜘蛛抓取图片时,Referer 到底带不带?
很多人以为蜘蛛爬网页时会带上上一级页面的 URL 作为 Referer。
实际上,各大搜索引擎的图片爬虫(比如 Baiduspider-image、Googlebot-Image)在抓取原始图片文件时,行为逻辑和普通用户在浏览器里顺着网页点击完全不同。
搜索引擎建立的是图片索引库。当蜘蛛的调度队列拿到了图片的绝对 URL,它发起的是独立的单点资源请求,而不是像无头浏览器那样先加载完整 HTML 再解析图片。这种抓取请求在很多情况下根本不带 Referer 请求头(也就是所谓的空 Referer)。
回到刚才那条配置:
valid_referers blocked server_names *.llbbs.cn;
这里虽然加了 blocked,但没有加 none。
在 Nginx 的定义里:
none:匹配请求头中完全缺少 Referer 的情况(比如浏览器直接在地址栏打开图片,或者爬虫独立抓取)。blocked:匹配请求头中有 Referer,但被防火墙或代理清空了(比如只传了空字符串或不合规字段)。
漏掉了 none,导致所有直接向图片 URL 发起的空 Referer 抓取请求全部被判定为 $invalid_referer = 1,搜索引擎蜘蛛抓一张死一张。
用 curl 模拟一下搜索引擎的行为就能清楚复现:
# 模拟没有 Referer 请求头的蜘蛛独立抓取
curl -I -A "Mozilla/5.0 (compatible; Baiduspider-image/2.0; +http://www.baidu.com/search/spider.html)" https://www.llbbs.cn/uploads/test.jpg
返回直截了当的 HTTP/1.1 403 Forbidden。
根因二:图片搜索展示页的 Referer 跨域降级
空 Referer 只是第一道坎。
当图片被收录进百度图片或者谷歌图片搜索结果页时,用户在搜索结果里点开预览,图片是从你的源站直接拉取展示的。
这时候浏览器会带上 Referer,但是 Referer 的域名是 https://www.baidu.com、https://image.baidu.com 或者 https://www.google.com。
更棘手的是现代浏览器的 Referrer-Policy 默认策略(通常是 strict-origin-when-cross-origin)。当搜索结果页发起跨域资源加载,有些环境送过来的 Referer 甚至只带来源协议和顶级域名,或者由于 HTTPS 到 HTTP 的跳转直接被浏览器剥离了。
如果你在 valid_referers 里面只写了自己站点的域名,那即便图片勉强进了索引,搜出来的缩略图在搜索结果页里也会全部变成破图,进而被搜索引擎判定为死链迅速剔除。
所以在配置合法域名白名单时,必须显式放行主流搜索引擎的域名:
valid_referers none blocked server_names *.llbbs.cn *.baidu.com *.google.com *.bing.com;
根因三:User-Agent 伪造与蜘蛛白名单的优先级
有人会想,既然搜索引擎蜘蛛那么特殊,能不能干脆写个针对 User-Agent 的例外规则?比如匹配到 Baiduspider 就直接放行?
早期我也见过有人这么写:
# 隐患写法:容易被采集站轻易穿透
if ($http_user_agent ~* (baiduspider|googlebot)) {
# 直接跳过防盗链
}
这种写法很容易留下大漏洞。采集站只要在爬虫脚本里把 User-Agent 伪造成 Baiduspider,防盗链立刻形同虚设。
更稳妥的做法是分层处理:
- 先利用 Nginx 的
valid_referers放行合法域名和空 Referer; - 对普通外链严格拦截,对搜索引擎保持开放;
- 如果是高风险资产,在边缘网关层结合 IP 段或双向反查做二次鉴权。
一套兼顾防盗链与 SEO 友好的 Nginx 配置
把上面的坑踩了一圈之后,目前在用的配置改成了这套方案:
# 在 server 块的静态资源 location 中
location ~* \.(jpg|jpeg|png|gif|webp|svg)$ {
# 1. 允许空 Referer、伪装 Referer、自身所有子域名,以及主流搜索引擎域名
valid_referers none blocked server_names
*.llbbs.cn
llbbs.cn
*.baidu.com
*.google.com
*.googleusercontent.com
*.bing.com
*.so.com
*.sogou.com;
# 2. 对非法 Referer 进行处理,但对已知正规蜘蛛 UA 保持兜底放行
set $block_hotlink 0;
if ($invalid_referer) {
set $block_hotlink 1;
}
# 如果非法 referer 但携带了常见搜索引擎爬虫标识,降级放行避免误杀
if ($http_user_agent ~* (Baiduspider|Googlebot|bingbot|Bytespider|360Spider|Sogou\ web\ spider)) {
set $block_hotlink 0;
}
if ($block_hotlink = 1) {
return 403;
}
# 静态资源缓存配置
expires 30d;
add_header Cache-Control "public, no-transform";
access_log off;
}
这段配置的核心在于两处细节:
第一,valid_referers 必须带上 none。不能为了封杀外链把没有 Referer 的单请求一律截断。
第二,搜索引擎白名单涵盖了 *.googleusercontent.com(谷歌图片缓存拉取常用域名)和国内搜狗、360 等常见源。
CDN 边缘节点的防盗链与源站冲突
如果你的静态资源前面挂了 CDN,必须注意 CDN 与源站的协同。
有一次我们在源站配好了放行规则,测试发现依然偶发 403。排查后发现是 CDN 边缘节点也开启了防盗链功能,而 CDN 控制台里的规则没有勾选“允许空 Referer”。
CDN 节点挡在源站前面,直接在边缘服务器就把搜索引擎蜘蛛的请求用 403 掐断了,请求根本没有到达源站 Nginx。
排查时别只盯着本机配置:
- 用
curl -I -H "Referer:" https://cdn.llbbs.cn/image.jpg模拟空 Referer 直接测 CDN 域名; - 检查 CDN 控制台的防盗链规则中,“允许空 Referer”和“放行爬虫”选项是否同时开启;
- 源站和 CDN 只保留一层主防盗链逻辑,避免两边规则互斥导致调试困难。
改完这套配置之后,观察了两周的站长后台日志,抓取错误率降到了个位数,百度和谷歌的图片收录曲线也逐步回升。防盗链防的是恶意采集,把搜索引擎爬虫放进来才能守住搜索流量。
文章标题:配置防盗链却把搜索引擎蜘蛛误杀了?我排查 Nginx valid_referers 与图片收录 403 的几个坑
文章链接:https://www.llbbs.cn/jishujaocheng/237.html
本站文章均为原创,未经授权请勿用于任何商业用途
评论一下?