搜索结果图标采集不到怎么办:favicon 完整排查与修复攻略

AI 摘要997笔记AI

图标变灰地球多与SEO无关,而是favicon文件、声明或MIME配置有坑。先诊断真缺失还是抓取失败,再补齐根目录图标、HTML声明,并警惕nginx的types{}替换全局导致类型错乱;必应抓取不走Bingbot日志。全套配置不足百行即可修复。

此内容由AI根据文章内容自动生成。

搜索结果里站点左侧那个小图标,三个月前还是自己的 LOGO,某天突然变成了一个灰扑扑的地球。绝大多数人这时候会做一件事:反复刷新、反复等、反复怀疑是不是被降权了。其实九成情况跟SEO 无关,是图标文件或声明方式有坑。

这篇记录我在 997笔记上的完整排查:从怎么确认「是真没图标还是工具没显示」,到站点根要放哪些文件、HTML 怎么声明、nginx 有个能把全站图片类型搞坏的坑,以及最关键的——必应图标为什么根本不看你的日志。全套配置不到 100 行,不改主题文件。

一、先诊断:分清「真没有」和「抓不到」

动手改之前,先分清三种完全不同的情况。搞混了会白折腾很久。

现象 真实原因 该做什么
搜索结果图标是灰色地球 搜索引擎没抓到你的图标 往下看,补全配置
浏览器标签页也是地球 HTML 声明缺失或路径 404 检查 link 标签和文件可达
昨天还有今天没了 多为工具侧反爬降级或缓存波动 换非隐私窗口复核,别改站点

先跑这三条命令拿到硬数据,别靠猜:

# 1. 站点根favicon 是否可达
curl -sI https://你的域名.com/favicon.ico | head -5

# 2. HTML 里声明了哪些 icon
curl -s https://你的域名.com/ | grep -o '<link[^>]*icon[^>]*>'

# 3. 逐个验证文件真实存在
for f in favicon.ico favicon-32.png apple-touch-icon.png site.webmanifest; do
  printf '%-24s ' "$f"
  curl -s -o /dev/null -w '%{http_code} %{content_type}n' "https://你的域名.com/$f"
done

重点看第三项的 content_type。很多新手只看到 200 就放心了,但文件返回 application/octet-stream 时,多数爬虫会直接放弃解析——这是最常见的隐藏坑,第四、五节专门讲。

二、搜索结果图标是怎么来的(原理)

先搞懂机制,才知道该改哪里。三个关键认知:

1. 大部分搜索引擎优先抓根目录的 /favicon.ico

这是历史约定。不是去解析你 HTML 里的 <link> 标签。所以只在后台设置站点图标、但根目录没有这个文件的话,搜索引擎大概率找不到。

2. 抓到了不等于会用

搜索引擎抓到图标后会做缓存,缓存里是一张 32×32 的小图。这张图一旦生成,很长时间不会更新——除非你主动催它。所以「配置全对了但还是地球」是正常状态,不是没配好。

3. 必应的图标抓取不走 Bingbot

这点很反直觉。检查服务器日志:

grep -i 'bingbot' access.log | grep -i 'favicon'
# 输出:空

是不是很慌?别慌,这是正常的。必应派了独立服务去抓图标,不以 Bingbot 身份请求,日志里自然看不到。所以「日志里没人来取 favicon」不能作为「我配置错了」的证据。

三、站点根要放哪些文件

一套完整配置需要这几个。缺哪个补哪个:

文件 必需 尺寸 / 格式 作用
favicon.ico 必需 16/32/48/64 多帧 搜索引擎的首选抓取目标
favicon-32.png 推荐 32×32 PNG HTML 里显式声明 32 尺寸
favicon-192.png 推荐 192×192 PNG 高分屏 / PWA
favicon-512.png 可选 512×512 PNG 后台选站点图标用
apple-touch-icon.png 推荐 180×180 不透明底 iOS 主屏图标
site.webmanifest 可选 JSON PWA 应用名与图标

apple-touch-icon 必须用不透明底。iOS 会把透明区域渲染成黑色,你的 LOGO 会被一块黑底包住。在生成时铺一层白底再合成:

bg = Image.new("RGBA", (512, 512), (255, 255, 255, 255))  # 白底
bg.alpha_composite(logo)                # 合成你的透明 LOGO
at = bg.resize((180, 180), Image.LANCZOS)
at.save("apple-touch-icon.png")

生成脚本(Python,可直接跑)

从一张 512×512 的透明底源图,一键派生全套:

from PIL import Image

im = Image.open("logo-512.png").convert("RGBA")

# 多帧 ICO —— 覆盖 16/32/48/64,各尺寸搜索引擎认得最清楚
im.save("favicon.ico", "ICO", sizes=[(s, s) for s in (16, 32, 48, 64)])

# 各尺寸 PNG
for s in (32, 192, 512):
    im.resize((s, s), Image.LANCZOS).save("favicon-%d.png" % s, "PNG")

# apple-touch-icon:白底,避免 iOS 黑块
bg = Image.new("RGBA", (512, 512), (255, 255, 255, 255))
bg.alpha_composite(im)
bg.resize((180, 180), Image.LANCZOS).save("apple-touch-icon.png", "PNG")

im.save("icon-512.png", "PNG")

manifest = {
    "name": "你的站名",
    "short_name": "站名",
    "icons": [
        {"src": "/favicon-192.png", "sizes": "192x192", "type": "image/png"},
        {"src": "/icon-512.png", "sizes": "512x512", "type": "image/png"},
    ],
    "theme_color": "#0b111c",
    "background_color": "#ffffff",
    "display": "standalone",
    "start_url": "/",
}
import json
json.dump(manifest, open("site.webmanifest", "w", encoding="utf-8"),
          ensure_ascii=False, indent=2)

四、🔴 最大的坑:nginx 的 types{} 是替换不是叠加

如果你想让 site.webmanifest 被浏览器正确识别,需要加 MIME 映射。然后你多半会写这么一段:

types {
    application/manifest+json  webmanifest;
}

看起来很无害。提交 nginx -t 通过,reload 成功。但刷新站点后会发现全站图片、JS、CSS 全都变成了 application/octet-stream——包括你刚做好的 favicon。

原因:nginx 的 types{} 块一旦出现,就会完全替换掉从全局 mime.types 继承的映射,而不是与它合并。你在 server 块里只写了一行,等于宣布「本站点只认 webmanifest 这一种类型,其他全部未知」。

还有一个更隐蔽的连带坑:很多人发现类型不对时会去加

default_type application/octet-stream;

这行更糟——它会把整个 mime.types 彻底顶掉,全站每个静态文件的 Content-Type 都变成 octet-stream。站点看着还能用(浏览器会猜类型),但搜索引擎的解析会全面受影响。

正确做法:显式列全你用到的类型,并去重

types {
    application/manifest+json  webmanifest;
    image/x-icon                ico;
    image/png                   png;
    image/jpeg                  jpg jpeg;
    image/gif                   gif;
    image/svg+xml                svg svgz;
    image/webp                  webp;
    image/avif                  avif;
    text/css                    css;
    text/javascript             js mjs;
    application/json            json map;
    text/xml                    xml;
    text/plain                  txt md;
    font/woff2                  woff2;
    font/woff                   woff;
    font/ttf                    ttf;
    font/otf                    otf;
}

同一扩展名只能映射一个类型。比如你既写 application/xml xml; 又写 text/xml xml;,nginx 会告警:

nginx: [warn] duplicate extension "xml", content type: "text/xml",
previous content type: "application/xml"

这不是致命错误,但会让启动输出变脏。养成改完跑 nginx -t 看有没有 warn 的习惯,做到零警告才算干净。

改完必须验证

nginx -t              # 语法 + 看有无 duplicate 警告
nginx -s reload
# 逐个核对 Content-Type,别只看 200
for f in favicon.ico favicon-192.png site.webmanifest; do
  printf '%-24s ' "$f"
  curl -sI "https://你的域名.com/$f" | grep -i '^content-type'
done

期望看到 image/x-icon、image/png、application/manifest+json。如果看到 application/octet-stream,就是踩了上面那个坑。

五、HTML 声明:顺序比数量重要

文件放好了,还得在 HTML 里声明。这里有个容易忽略的细节:浏览器和爬虫取 sizes="32x32" 时,读的是第一条匹配的声明。所以把正确的那条放前面,别指望「后面那条也能被认到」。

顺序上还有一层:WordPress 主题通常在 wp_head 里输出自己的图标声明。如果你的声明排在主题之后,优先级就低了。

优先级 挂载点 效果
高 mu-plugin 挂 wp_head 优先级 2 排在主题模板之前,推荐
低 主题 header.php 直接改 能用,但主题升级即失效

完整 mu-plugin(不改主题,升级不失效)

<?php
/**
 * Plugin Name:站点 favicon 增强
 * Description: 补齐主题未输出的 favicon 声明。
 * Version: 1.0
 */

if (!defined('ABSPATH')) {
    exit;
}

final class N997_Favicon
{
    public static function init()
    {
        add_action('wp_head', array(__CLASS__, 'printTags'), 2);
    }

    public static function printTags()
    {
        if (is_admin() || (defined('DOING_AJAX') && DOING_AJAX)) {
            return;
        }
        $home = home_url('/');
        $lines = array();

        // .ico 直指 —— 搜索引擎图标服务的标准抓取目标
        $lines[] = '<link rel="icon" type="image/x-icon" sizes="32x32" href="' . esc_url($home . 'favicon.ico') . '">';
        $lines[] = '<link rel="icon" type="image/png" sizes="32x32" href="' . esc_url($home . 'favicon-32.png') . '">';
        $lines[] = '<link rel="icon" type="image/png" sizes="192x192" href="' . esc_url($home . 'favicon-192.png') . '">';

        // apple-touch-icon —— 根目录白底版,避免透明底被压黑
        $lines[] = '<link rel="apple-touch-icon" sizes="180x180" href="' . esc_url($home . 'apple-touch-icon.png') . '">';

        // PWA
        $lines[] = '<link rel="manifest" href="' . esc_url($home . 'site.webmanifest') . '">';
        $lines[] = '<meta name="theme-color" content="#0b111c">';

        echo "n" . implode("n", $lines) . "n";
    }
}

N997_Favicon::init();

放到 wp-content/mu-plugins/ 目录下(不是普通插件目录)。mu-plugin 会被自动加载、后台无法禁用、主题更新也不会覆盖它。

六、验证:4 个命令确认到位

# 1. 第一条 32x32 是不是 .ico(关键)
curl -s https://你的域名.com/ | grep -o '<link[^>]*sizes="32x32"[^>]*>' | head -1
# 期望:href=".../favicon.ico"

# 2. Content-Type 全部正确
curl -sI https://你的域名.com/favicon.ico | grep -i content-type
# 期望:image/x-icon

# 3. nginx 无警告
/www/server/nginx/sbin/nginx -t 2>&1 | grep -c warn
# 期望:0

# 4. 浏览器实际取到的图标
#    打开首页 → F12 → Network → 过滤 favicon → 看第一个请求

七、必应图标刷新的真实机制

这是新手最容易被坑的地方:配置全对,图标还是地球。

原因很简单:搜索引擎把图标缓存了。缓存的刷新由它自己决定,通常几天到两周。配置正确只是「让下次刷新时能抓到新图」,不能立刻生效。

能做什么加速

  • 必应站长工具 → URL 检查:提交首页 URL,额度有限,用在真正重要的页面上。
  • IndexNow 协议:必应官方推送接口,不消耗 URL 检查额度。IndexNow 规范只接受 HTML 页面 URL,所以能催页面抓取,但对 favicon 图标缓存无直接作用。

预期管理很重要。把 favicon 配置做好 = 排除了一个故障因素,剩下交给时间。如果配置不全,等两周也是白等;配置全了,两周内大概率自己就变了。不要因为「今天还是地球」就反复改动站点——每次改动都可能触发新的缓存失效。

八、一句话总结

搜索结果图标采集不到,按这个顺序查:

  1. 确认是不是真问题 —— 换个非隐私窗口查,排除工具反爬降级
  2. 站点根放齐文件 —— favicon.ico(多帧)+ 32/192 PNG + apple-touch(白底)
  3. 核对 Content-Type —— 不只是看 200,要看类型对不对
  4. HTML 补声明 —— mu-plugin 挂 wp_head 优先级 2,确保 sizes="32x32" 第一条是 .ico
  5. 避开 nginx 坑 —— types{} 是替换不是叠加,别加 default_type,改完跑 nginx -t 看零警告
  6. 然后等 —— 缓存刷新由搜索引擎自己决定,一到两周

整套配置不到 100 行、一个 mu-plugin 文件、一次 nginx 改动。剩下的就是交给时间。

上一篇 WordPress 首页没有 H1 标记怎么修:AeroCore 主题实测修复全过程
下一篇 不埋点也能做网站访问统计看板:解析 nginx 日志全流程