清了浏览器缓存,按了刷新,结果 favicon 还是白板一块。遇到这种 favicon 清缓存后依然不显示的问题,很多人第一反应是骂浏览器,其实真正的锅往往在服务端。

上周二我在客户的预发环境上跟这个问题死磕了三个小时。我把 Chrome 的 favicon 数据库都删了,机器重启,还是白搭。最后发现根本不是浏览器缓存的事。

下面直接上最快的解法,然后再聊聊根本原因,帮你彻底避开这个坑。

快速解法:绕过浏览器缓存

浏览器死活不肯拉取新图标,那就强制发一个唯一的请求。在 favicon 路径后面加个 query string,浏览器就会当成一个全新文件去下载。

把 HTML 里的 <link> 标签改成带版本号的形式:

<link rel='icon' href='/favicon.ico?v=20260115' type='image/x-icon'>

每次换图标就改一下版本号。这是最靠谱的强制刷新方式,不用让用户去清浏览数据。想深入了解长期策略,可以看我们这篇 favicon cache busting best practices。

为什么清缓存不管用

浏览器这东西很倔。你清缓存,清的只是 HTTP cache。favicon 往往存在另一个独立的持久化数据库里,常规清缓存根本碰不到它。

藏在暗处的 SQLite Favicon 数据库

Chrome 和 Firefox 都有一个专门的 SQLite 数据库来存 favicon。这个数据库跟浏览历史和书签是绑定的,所以你清标准缓存它不动。只要浏览器觉得 favicon 的 URL 没变,它就不会重新拉取。

这就是为什么你把浏览数据都核弹级清除了,图标还是老样子。浏览器拿硬编码的 URL 去 SQLite 库里一查,命中了,直接返回旧文件。

Service Worker 拦截

如果你在搞 PWA,service worker 可能直接从 Cache API 里把旧 favicon 吐出来了。清浏览器缓存根本动不了 service worker 的缓存。

解法是去 DevTools 里手动注销 service worker(Application > Service Workers > Unregister),或者改你的 service worker 文件,把旧的缓存条目删掉。

根本原因:服务端该查什么

如果加 query string 这招都不管用,说明服务端在主动拦截或者路由有问题。下面是我最常踩的三个服务端坑。

1. CDN 边缘缓存太猛

你的 CDN 可能在给全球用户吐旧文件。像又拍云、七牛、Cloudflare 这类,默认会把 favicon.ico 这种静态资源缓存很久。

你得去 CDN 控制台手动 purge 掉 favicon 的缓存,或者给图标文件设短一点的 TTL。

2. MIME Type 没配对

服务端不告诉浏览器文件类型,现代浏览器直接拒收。我见过不少 Nginx 和 Apache 配置,把 .ico 文件返回成 application/octet-stream,Chrome 直接静默忽略。

确保 Nginx 配置里有正确的 MIME type:

types {
    image/x-icon ico;
    image/svg+xml svg;
}

3. CORS 策略太严

如果你把 favicon 放在子域名或者专门的静态资源桶上,CORS 策略可能把它挡了。响应里没有正确的 Access-Control-Allow-Origin header,浏览器就不渲染 favicon。

掘金在这方面做得不错。他们的 SVG favicon 托管在独立的 CDN 域名上,但 CORS header 配好了,允许主域名拉取。想看大厂怎么处理的,去 inspect 掘金标签页图标的 network 请求,header 精准到位,图标秒加载,没有安全警告。

2026 年怎么根治这个毛病

别再指望手动清缓存了,这是打不赢的仗。你需要一套部署策略,让旧 favicon 根本没机会存活。

上线前也要验证一下配置。用 Mzu favicondl 这种工具跑一下,确认服务端返回的 header 和文件类型没问题。

别猜了,去验证

favicon 清缓存后不显示,最烦的地方在于你以为是浏览器坏了,其实浏览器只是在执行服务端或自己 SQLite 库里的旧指令。

加 query string 强制下载,查 service worker,确认 CDN 和 MIME type。想看更全面的图标丢失排查,读这篇 favicon not showing 指南。

如果你想跨平台验证图标配置,不用瞎猜,把 URL 丢进 Mzu favicondl 跑一下,用户看到什么你就看到什么。