Safari 为什么拒绝 Hugging Face 的 favicon:一次 ICO 分层实验
Hugging Face 的 favicon 在 Chrome 里一直正常,到了 Safari 却只剩下默认图标。最开始我以为又是缓存坏了:重建缓存、删除拒绝记录、重新访问,结果完全没变。
这次的问题和我之前处理过的 Safari Touch Icon 不是一回事。Touch Icon 缓存里,Hugging Face 已经有一张下载成功的 128×128 PNG;真正出问题的是 Favicon Cache,官方 ICO 只留下了一条拒绝记录。
我原本只想把那个黄色小脸找回来,最后却做成了一次 ICO 分层实验。
9 月 19 日补记:后来我又查了 X.com 上“旧页面有图标,新页面没有”的问题。这次图片本身正常,最终通过刷新共享图标记录的时间戳并清除对应拒绝状态恢复了。实验和可复现 SQL 放在本文后半部分。
先把网络和旧缓存排除掉
Hugging Face 当前的 官方 favicon 是一个 205,556 字节的 ICO,内部有 9 张图:
1 | 16、24、32、48、64、72、96、128、256 px |
服务器能正常返回文件,Chrome 也能显示。Safari 这边即使删除拒绝记录,下一次访问还是会重新拒绝。把完全相同的网页、响应头和 ICO 搬到本机 HTTP 服务后,结果也一样。
这一步很重要。故障会跟着图片来到 localhost,说明它不是 Hugging Face 域名、CDN 或跨站访问造成的,也不只是某条陈旧缓存没清干净。
拆开 ICO,Safari 的边界才露出来
我保留原始像素,只重建 ICO 的目录、数量和偏移,然后让 Safari 分别读取不同组合。结果比预想中有意思:
| ICO 内容 | Safari 结果 |
|---|---|
| 官方 9 层 | 拒绝 |
| 去掉 96px 后的 8 层 | 拒绝 |
| 16~96px 共 7 层 | 拒绝 |
| 16~72px 共 6 层 | 接受 |
| 16、24、32、48、64 共 5 层 | 接受 |
| 16、32、48 共 3 层 | 接受 |
| 单独的 24、32、72、96、128 或 256px | 都接受 |
单张 256px 能过,说明“Safari 不支持大图”说不通。我还把一份只有 32px 的 ICO 补零到 205,556 字节,它照样成功,所以总文件大小也不是原因。
这组样本只能说明:Safari 的 favicon 处理会拒绝这份多图 ICO;在我的组合里,6 层成功,7、8、9 层失败。 它没有证明 Safari 对所有 ICO 都存在一个通用的“最多 6 张”规则。编码方式、目录排列或某些层之间的组合仍可能参与判断。
至少排查范围已经很窄了。问题发生在 Safari 的 favicon 解码或采用阶段,不在图片能不能下载。
页面脚本也救不了
既然官方 ICO 会被拒绝,我试过从页面侧换一个图标。通过 PageRune 在 document-start 注入 link rel="icon",先用 PNG,后来又换成 Hugging Face 官方 SVG。脚本显示已执行,Safari 检查器里也能看到新节点确实进入了 document.head,标签页图标还是没变。
这个现象并不新鲜。WebKit 的 Bug 95979 讨论的就是用 JavaScript 修改 favicon 链接后不重新加载的问题。当年的讨论还明确提到,Apple 平台并不打算默认开放脚本动态修改 favicon。
我因此停掉了脚本。DOM 里有一条正确的链接,不等于 Safari 会采用它;继续堆 MutationObserver 只是在证明同一件事。
最后的修复落在 Favicon Cache
我从本机已经通过 Safari 测试的 16/32/48 组合做了一份 兼容 ICO。不过 Hugging Face 页面本身并没有引用它,扩展又无法让 Safari 动态换图,所以最后只能处理 favicon 缓存。
动手前我先完全退出 Safari,并备份:
1 | ~/Library/Safari/Favicon Cache/favicons.db |
缓存里的图标文件不是按网址命名,而是与一条 UUID 记录关联。我的做法是复制 Safari 已经接受的 16/32/48 ICO,创建新的 UUID,再把 UUID 字符串的 MD5 转成大写,作为 favicons 目录里的文件名。随后在数据库里:
- 为 Hugging Face 官方 favicon URL 写入一条
icon_info,尺寸和标记沿用已验证样本; - 在
page_url中把主页映射到新的 UUID; - 只删除 Hugging Face 对应的拒绝记录;
- 对比修改前后的非 Hugging Face 行,确认其他站点没有变化;
- 最后执行 SQLite
quick_check,结果为ok。
这不是我愿意公开成“一键修复脚本”的东西。Safari 没有公开这套数据库结构,而且当时的脚本依赖本机测试产生的源 UUID。把它包装成通用工具,风险比价值大。
图标回来了,但先观察
重新打开 Safari 后,Hugging Face 首页的 favicon 出现了,刷新后仍然保留。继续访问 /models,图标也正常显示,Safari 还自动给这个页面建立了映射。数据库里已经没有 Hugging Face 的拒绝记录。
9 月 19 日:X 的图片没坏,新页面却关联不上
Hugging Face 恢复以后,我又遇到了 X 的图标问题。已有首页能显示 X,新打开的推文却是默认地球图标。我起初以为是推文详情页的特殊行为,但给首页加一个新的查询参数,Safari 同样无法给这个新 URL 关联图标。
这轮观察到的 Safari 版本是 27.2。页面使用的共享图标地址是 twitter.3.ico。虽然扩展名是 .ico,实际响应却是 549 字节的 32×32 PNG,和 x.com/favicon.ico 的内容完全相同。Safari 自己生成的缓存文件是 16/32px 的 ICO 表示,几份成功与失败记录对应的图片字节也一致。
所以不能把 Hugging Face 的多图 ICO 结论搬过来。X 的原图可以正常使用,旧页面也确实还在显示它,异常发生在新页面取得这条共享图标的过程中。
我用最小 HTML 做了对照,只改变图标 URL:
| 对照 | 图标显示与页面关联 |
|---|---|
| 引用原 CDN 图标 URL | 默认图标,没有新映射 |
| 同一个图标 URL 加查询参数 | 立即显示 X,自动建立映射 |
| 第二个新页面继续引用这个带参数的 URL | 正常显示,复用同一条图标记录 |
| 引用 X 根目录的 favicon URL | 正常显示,自动建立映射 |
失败跟着原图标 URL 来到了最小页面,已经不需要推文内容或 X 的页面路由参与。
清掉拒绝记录,还差一步
最初那些失败页面甚至没有各自的拒绝行。后续修复实验中,共享图标 URL 才产生了拒绝记录;只盯着某条推文的 URL 查缓存,很容易漏掉这一层。
我先试过重启,也试过重建共享记录。中间有一次恢复旧记录后仍然失败,但后来发现 Safari 已经回收了那张暂时失去引用的旧图片。那次同时改变了记录和文件是否存在,不能拿来单独证明时间戳有问题。
于是我从备份恢复原图片,核对原 UUID、原时间戳和文件 SHA-256,再做对照:
| 保持完整原图与原 UUID | 结果 |
|---|---|
| 保留旧时间戳,只清除该图标 URL 的拒绝记录 | 新推文仍没有映射,并再次生成拒绝记录 |
| 只将时间戳更新为当前值,再清除该图标 URL 的拒绝记录 | 新推文显示 X,Safari 自动创建映射 |
随后我又打开第二条此前没有关联过的新推文,界面上也出现了 X,数据库里多出了自动映射。最终该共享图标 URL 的拒绝行数为零。修复保留了原 UUID、原图片字节和已有 page_url 映射,没有逐条添加推文地址。
这让我更倾向于把问题理解为:共享图标记录过期后,重新验证路径与拒绝状态一起影响了新页面关联。这是根据对照作出的推断;Safari 内部具体在哪一步失败、多久算过期,我还没有查明。
修复过程
这组 SQL 适用于本机已经有完整 X 图标缓存、表结构也与实验一致的情况。先用 ⌘Q 正常退出 Safari,确认进程已经退出,再打开缓存数据库。
1 | pgrep -x Safari |
进入 SQLite 后,先备份再检查。下面的备份文件名是示例,选一个尚不存在的名字,保存在自己知道的位置;.backup 会保存当前逻辑数据库,包括 WAL 中的数据。
1 | .backup 'favicons-before-x-refresh-20260919.db' |
确认 quick_check 为 ok,icon_info 有 url、timestamp 字段,rejected_resources 有 icon_url 字段,并且精确 URL 对应的现存图标记录符合预期。还要检查这条记录引用的缓存图片:本次 Safari 使用 UUID 字符串的大写 MD5 作为 favicons 子目录中的文件名,图片应存在且能解码。只看见数据库行不够;如果文件已丢失或损坏,下面的时间戳更新不能补回图片。
检查通过后,在同一个 SQLite 会话运行:
1 | BEGIN IMMEDIATE; |
先核对更新行数与刚才查到的目标记录数一致,再执行 COMMIT;;如果不符合预期,执行 ROLLBACK;。这里的 978307200 是 Unix 1970 年纪元到 Apple 2001 年纪元的秒数差,不能直接把 Unix 时间写进去。
提交后检查并退出:
1 | PRAGMA quick_check; |
重新启动 Safari,访问此前没关联过图标的 X 页面,看图标是否出现,再检查 Safari 有没有自动写入 page_url。这才是本次修复的验收点。已有页面原本就能显示,单看它刷新成功不能说明新页面的问题已解决。
这次操作只触及精确图标 URL 的时间戳和拒绝记录,不需要复制别人的 UUID、图片或整份数据库。我的两个新页面目前都通过了验证;是否长期不再复发,还要继续观察。
Safari 为什么拒绝 Hugging Face 的 favicon:一次 ICO 分层实验
https://blog.khan.moe/2026/09/16/Safari为什么拒绝Hugging-Face的favicon:一次ICO分层实验/




