实施图片懒加载后,发现搜索引擎图片收录归零,原因在于搜索引擎爬虫无法解析JavaScript,导致图片未正常加载。解决方法包括使用`noscript`标签和原生的`loading="lazy"`属性,并确保图片的`width`和`height`属性明确声明,以避免累积布局偏移影响搜索排名。
为了死磕移动端 PageSpeed 分数,前阵子我把全站模板里的图片标签全部改成了前端懒加载。本地用 Chrome 审查元素测试时体验极好,首屏加载直接少了几兆的流量,Lighthouse 性能得分也从 72 分一口气窜到了 95 分。
本以为这是一次教科书级别的前端性能调优,结果三个星期过去,去翻百度搜索资源平台和 Google Search Console 的数据后台,当场傻眼:站内原先每天能带来几百次点击的图片搜索流量直接断崖式下滑,索引量几乎降到了个位数。更难受的是,几篇原本排在首页的技术长文,综合排名也掉了好几个名次。
把网页源码和 Nginx 日志扒了一遍,我才发现自以为聪明的性能优化,实际上把搜索引擎爬虫的抓取链路拆得千疮百孔。
爬虫不滚屏:IntersectionObserver 与 data-src 的视口盲区
主流的前端懒加载库(比如 lazysizes、lozad 或者自己写的 IntersectionObserver 监听脚本),工作原理都很直白:把图片的真实地址藏在 data-src 属性里,src 属性只塞一张 1 像素的 Base64 透明图或者统一的占位图。当用户手指往下滑动页面,图片元素进入浏览器可视区域时,JS 脚本把 data-src 的值赋值给 src,浏览器才去发请求下载图片。
<!-- 常见的前端懒加载代码结构 -->
<img src="data:image/svg+xml;base64,PHN2ZyB..." data-src="/uploads/2026/04/k8s-arch.png" class="lazyload" alt="Kubernetes 架构拓扑图">
对真实人类用户来说这套逻辑很顺畅,但搜索引擎蜘蛛完全是另一套规矩:
- 很多爬虫(特别是百度的常规抓取节点)优先解析静态 HTML 文本,根本不会去执行复杂的 JS 脚本。在它的抓取视角里,全站所有文章的图片全都是那张 1 像素的 Base64 占位图。由于成百上千篇页面的图片源码一模一样,爬虫甚至会判定页面存在大量重复低质资源。
- 即使是支持 Headless 渲染的爬虫集群(比如 Googlebot),在渲染阶段分配给单个页面的计算时间和内存极其有限。爬虫渲染器打开页面时默认视口通常是固定的(例如 1024x768),它为了节约服务器资源,根本不会触发浏览器的
scroll滚动事件,也不会模拟鼠标滚轮一直往下拉到底。
这意味着,首屏第一屏以外的所有 data-src 图片,永远都等不到被 JS 赋值为真实 src 的那一刻。
怎么给爬虫留后门:noscript 标签兜底与原生懒加载
如果页面必须保留 JS 懒加载逻辑,最稳妥的兼容方案是给每个图片节点补上 <noscript> 标签:
<!-- 带 noscript 兜底的懒加载写法 -->
<img src="data:image/svg+xml;base64,PHN2ZyB..."
data-src="/uploads/2026/04/k8s-arch.png"
class="lazyload"
alt="Kubernetes 架构拓扑图"
width="800"
height="450">
<noscript>
<img src="/uploads/2026/04/k8s-arch.png"
alt="Kubernetes 架构拓扑图"
width="800"
height="450">
</noscript>
当不支持执行 JavaScript 或者跳过脚本渲染的搜索引擎爬虫抓取网页时,它会跳过外层脚本,直接解析 <noscript> 内部的标准 HTML。Google 和百度的官方 SEO 文档里都明确说明过:<noscript> 里的图片只要 URL 和文本描述真实对应,会被正常视作页面的有效媒体内容参与索引。
更省事的现代方案是丢掉笨重的第三方 JS 库,改用现代浏览器原生支持的 loading="lazy":
<img src="/uploads/2026/04/k8s-arch.png"
loading="lazy"
alt="Kubernetes 架构拓扑图"
width="800"
height="450"
decoding="async">
原生属性不需要写任何 JS 监听代码,浏览器自身会根据网络状况和视口距离自动预加载下方的图片,而爬虫在抓取 HTML 阶段就能直接拿到清晰明确的 src 原图地址。
占位图引发的 CLS 灾难:为什么图片必须写死 width 和 height
改了懒加载之后,很多人只关注了页面体积变小,却忽视了 Core Web Vitals 里的另一个关键指标:CLS(Cumulative Layout Shift,累积布局偏移)。
很多人的占位图写的是 1x1 像素的小图,或者在 CSS 里只给图片加了 max-width: 100%,却没给 <img> 标签声明原始的 width 和 height 属性:
<!-- 典型的错误写法:没有尺寸声明 -->
<img src="/uploads/arch.png" loading="lazy" alt="架构图">
在图片下载完成前,浏览器根本不知道这张图的宽高比是多少,排版引擎会把图片的高度先算作 0。等到用户或者爬虫渲染器把图片下载回来,原本高度为 0 的位置猛地撑开一块 500 像素高的区域,整篇文章下半截的段落瞬间被强行往下挤开。
这种剧烈跳动会导致页面的 CLS 评分直接飙升到 0.3 以上(Google 官方的良好标准是低于 0.1)。现代搜索引擎对移动端页面体验(Page Experience)的评分非常敏感,布局剧烈抖动的页面会被算法判定为交互体验低劣,进而影响搜索排名。
解决办法是在 HTML 里显式写明原图的像素宽高,或者在 CSS 里用 aspect-ratio 预留空间:
<img src="/uploads/2026/04/k8s-arch.png"
alt="Kubernetes 架构拓扑图"
width="800"
height="450"
style="width: 100%; height: auto; aspect-ratio: 16 / 9;"
loading="lazy">
只要声明了 width="800" 和 height="450",即便现代 CSS 把宽度拉伸为响应式的 100%,现代排版引擎也能在图片尚未下载时,提前根据比例在 DOM 树里占好对应高度的坑位,后续无论何时加载都不会发生一丁点布局抖动。
CDN 防盗链误杀图片蜘蛛:Baiduspider-image 的 403 惨剧
排查日志时,我还揪出了一个特别隐蔽的运维配置失误。
我在 CDN 和对象存储上开启了防盗链功能,只允许 *.llbbs.cn 作为访问来源。当时顺手把“允许空 Referer”的勾给取消了,觉得这样防盗链更彻底,别人休想盗用外链。
结果坏事就坏在这里。
用户在浏览器里打开网页时,浏览器请求图片会自动带上当前页面的 Referer 头(例如 Referer: https://www.llbbs.cn/article/123.html),所以自己打开网站测试时图片一切正常,没有任何裂图。
但搜索引擎的图片专用抓取蜘蛛(比如百度的 Baiduspider-image、谷歌的 Googlebot-Image)去抓取原图时,请求头里经常是不带 Referer 的,或者 Referer 并非本站域名。防盗链策略一匹配,CDN 边缘节点直接毫不客气地甩给爬虫一个 403 Forbidden。
模拟爬虫验证防盗链响应
排查这种问题不需要猜,直接用 curl 模拟爬虫的 User-Agent 并且不带 Referer 请求图片地址:
# 模拟百度图片爬虫,空 Referer 请求测试
curl -I -H "User-Agent: Baiduspider-image+(+http://www.baidu.com/search/spider.htm)" \
https://www.llbbs.cn/content/uploadfile/202604/k8s-arch.png
如果返回的是:
HTTP/2 403
date: Tue, 06 Oct 2026 14:20:10 GMT
content-type: text/html
那就证明爬虫已经被你的防盗链规则当成盗链盗刷请求直接干掉了。
在 Nginx 或者 CDN 规则里,防盗链配置必须把主流爬虫加进白名单,或者强制允许空 Referer:
location ~* \.(jpg|jpeg|png|webp|gif|svg)$ {
# 检查 Referer,允许空来源以及本站域名
valid_referers none blocked server_names *.llbbs.cn;
# 针对爬虫 User-Agent 额外放行
if ($http_user_agent ~* (Baiduspider|Googlebot|bingbot|Bytespider|360Spider)) {
set $valid_referer 1;
}
if ($invalid_referer) {
return 403;
}
expires 30d;
add_header Cache-Control "public, no-transform";
}
配置更新之后再用上面的 curl 命令跑一次,确认 HTTP 状态码变成 200 OK 才是正常状态。
Nginx 动态转 WebP 漏掉 Vary 响应头:CDN 缓存投毒与爬虫识别
为了把体积压得更小,很多站长会在 Nginx 层根据客户端请求头里的 Accept 字段,把 .jpg 或 .png 动态重写到经过 cwebp 预生成的同名 .webp 文件。
我的 Nginx 早期配置类似这样:
# 存在缺陷的 WebP 自动重写配置
location ~* ^(/content/uploadfile/.+)\.(png|jpe?g)$ {
set $img_path $1;
if ($http_accept ~* "webp") {
rewrite ^ $img_path.webp break;
}
}
这个写法如果网站直连单台源站,问题还不明显。可一旦前面套了 CDN 或者反向代理缓存,就会引发灾难性的缓存投毒:
- 某个支持 WebP 的 Chrome 浏览器率先访问了一张图片,Nginx 返回了 WebP 格式的文件内容。
- CDN 边缘节点把这份二进制数据缓存了下来,缓存 Key 是 URL
https://www.llbbs.cn/.../arch.png。 - 随后一个请求头里不包含 WebP 支持的老版本爬虫来抓取该 URL,CDN 直接把刚才缓存好的 WebP 二进制数据扔了过去。
- 爬虫解析数据时,发现文件扩展名明明标着
.png,但文件头签名字节却是RIFF...WEBP。很多搜索引擎爬虫的格式校验机制比较死板,检测到文件扩展名与二进制魔数不匹配时,会直接判定文件损坏并丢弃索引。
必须补上的 Vary: Accept 响应头
解决这个问题的标准做法是在返回重写内容时,必须给响应头附带 Vary: Accept,告诉中间的所有 CDN 和浏览器代理缓存:同一个 URL 必须根据请求方的 Accept 头内容分别独立缓存:
location ~* ^(/content/uploadfile/.+)\.(png|jpe?g)$ {
add_header Vary Accept always;
# 如果客户端明确支持 webp 且本地存在对应 webp 文件才重写
if ($http_accept ~* "image/webp") {
rewrite ^(.*)\.(png|jpe?g)$ $1.webp break;
}
expires 60d;
add_header Cache-Control "public, max-age=5184000, immutable";
}
在 HTML 层面,更规范的做法是直接在前端使用 <picture> 标签提供多格式分发:
<picture>
<source srcset="/uploads/2026/04/k8s-arch.webp" type="image/webp">
<img src="/uploads/2026/04/k8s-arch.png"
alt="Kubernetes 架构拓扑图"
loading="lazy"
width="800"
height="450">
</picture>
无论遇到什么爬虫,<picture> 标签内部的 <img> 都是符合 Web 标准的基础兜底节点,爬虫能够准确提取到兼容性最好的原始图片地址,不会产生格式歧义。
检查 Nginx 日志排查爬虫抓图状态
改完上述所有配置后,不要干等搜索引擎更新。在服务器终端跑两条 grep 命令,就能直接看清这几天搜索引擎图片蜘蛛抓取图片的真实状况:
# 过滤近期的百度图片蜘蛛访问日志
grep "Baiduspider-image" /var/log/nginx/access.log | awk '{print $7, $9}' | tail -n 20
正常情况下,输出里的每一行都应该是真实的图片路径后紧跟着 200 或 304:
/content/uploadfile/202604/k8s-arch.png 200
/content/uploadfile/202604/mysql-index.png 200
/content/uploadfile/202604/nginx-flow.png 304
如果日志里大面积出现 403、404 或者是占位图的单一路径,那就说明配置依然有遗漏。
折腾前端性能调优,不能光盯着本地 Lighthouse 跑分高兴。搜索引擎爬虫终究不是真实的人类浏览器,它不会为了省几百 KB 流量去执行复杂动画,也不会在 403 防盗链面前想办法绕过。把 noscript 兜底补齐、把尺寸占位留准、把防盗链白名单放开,才能在提速的同时,保住搜索引擎的收录基本盘。
文章标题:网站上了图片懒加载,搜索引擎图片收录却彻底归零?排查 noscript 兜底、防盗链误杀爬虫与 WebP 适配我踩过的几个坑
文章链接:https://www.llbbs.cn/jishujaocheng/229.html
本站文章均为原创,未经授权请勿用于任何商业用途
评论一下?