favicon の更新・キャッシュ問題の調査
faviconが更新されないときはバージョンクエリでURLを変える
約2か月前
ブラウザはfaviconをURL単位で極端に強くキャッシュし、Cache-Controlヘッダーを無視することが多い。特に/favicon.icoはlinkタグと無関係にブラウザが直接取得し、貼り付きやすい。
そのため、アイコン差し替え時にキャッシュヘッダーを設定するだけでは、すでにキャッシュ済みの古いアイコンは消えないことがある。確実なのはアイコンURLにバージョンクエリ(例: ?v=2)を付けてURL自体を変えること。差し替えのたびに数値を上げる。
併用パターン:
- ヘッダー(
Cache-Control: public, max-age=0, must-revalidate)で今後の再検証を保証(再発防止) - バージョンクエリでURLを変え、今のキャッシュを即時無効化
CDN/リバースプロキシ経由時はそちらのキャッシュパージも別途必要。
Google s2 favicon は外部キャッシュと content-location を確認して切り分ける
約2か月前
ブラウザや外部アプリで favicon が更新されない場合、サイト本体の /favicon.ico や manifest アイコンが最新でも、Google の https://www.google.com/s2/favicons?domain=... は t*.gstatic.com にリダイレクトされ、独自の Cache-Control でキャッシュされる。curl -sSL -D headers.txt -o icon.png 'https://www.google.com/s2/favicons?domain=example.com&sz=96' を実行し、レスポンスヘッダーの content-location と cache-control を確認すると、Google がどのアイコン URL を採用しているか、更新待ちなのかを切り分けられる。
Electron アプリの favicon 表示は Application Support 配下の Chromium cache を確認する
約2か月前
Electron アプリで外部サイトや連携先の favicon 表示が更新されない場合、アプリの起動引数やプロセス情報から user-data-dir を特定し、その配下の Cache/Cache_Data を確認する。macOS の Claude Desktop では ~/Library/Application Support/Claude/Cache/Cache_Data に Google s2 favicons や gstatic faviconV2 のレスポンスが保存されることがある。rg -a -l 'domain|faviconV2|google.com/s2/favicons' <user-data-dir>/Cache/Cache_Data や strings <cache-file> で content-location、expires、cache-control を確認すると、アプリ側ローカルキャッシュなのか外部サービス側キャッシュなのかを切り分けられる。
Next.js App Router の app/favicon.ico は自動で favicon metadata を生成する
約2か月前
Next.js App Router では app/favicon.ico などの特殊ファイルを置くと、HTML head に favicon 用の metadata/link が自動生成される。layout.tsx の metadata.icons でも favicon を明示すると link rel="icon" が重複することがある。favicon URL を1本に統制したい場合は、favicon 実体を public/favicon.ico などの通常静的ファイルとして配置し、metadata.icons から明示的に参照する構成にすると自動生成と競合しにくい。
faviconクローラー向けのルートはGETだけでなくHEADも成功させる
約1か月前
favicon が外部サービスやディレクトリ表示に反映されないとき、/favicon.ico 自体が 200 でも、クローラーが最初に確認するルート URL の HEAD / や GET / が 4xx/405 を返していると、アイコン発見に失敗することがある。
特に API サーバーや MCP/JSON-RPC など「通常は POST だけを受ける」エンドポイントをドメインルートに置く場合、ブラウザやクローラー向けの通常 GET / と HEAD / には最小 HTML もしくは同等のヘッダーを 200 で返し、アイコン参照タグ(link rel=icon)や apple-touch-icon を発見できるようにする。
判断基準:
GET /は favicon link 付きの最小 HTML を返す。HEAD /も 200 とcontent-type: text/htmlなどの発見に必要なヘッダーを返し、本文は返さない。- プロトコル上意味のある特殊な GET(例: SSE の
Accept: text/event-stream)は既存仕様を維持する。 - 未知パスや本来拒否すべきメソッドまで一律 200 にしない。発見性のための例外はルートとアイコンパスに限定する。
検証方法は、外部 favicon サービスだけを見るのではなく、まずルート(/)への HEAD と GET、および /favicon.ico への HEAD を順に叩き、配信側が 2xx を返すか確認する(curl の -I/-i で確認できる)。外部サービスがまだ古い結果を返す場合は、そのサービスのキャッシュや再クロール待ちとして切り分ける。
Google s2 favicon は http:// キーで内部キャッシュする — bare domain は http に寄り、http 側がクロールされるまで更新されない
約1か月前
Google の s2 favicon 取得エンドポイントは、裸のドメインを domain パラメータで渡すと、内部の faviconV2 では http スキームの URL キーへ正規化されることがある。サイト本体が HTTPS canonical で、HTTP から HTTPS へ正常に恒久リダイレクトしていても、Google 側の favicon キャッシュは http キーと https キーで反映状態が別々に見える。
実例として、HTTPS 側は正しい新アイコンを返すのに裸ドメイン指定(=http キー)だけが古いまま、という状態が長く続いた。最終的な原因は「http スキーム側の URL が favicon 専用クローラにクロールされていなかった」こと。Google は内部的に http の URL キーで favicon をキャッシュしており、http 側が再クロールされるまで裸ドメインの結果は更新されなかった。http 側がクロールされたタイミングで反映された。つまり、リダイレクト設定が正しくても、favicon の反映は http キーのクロール待ちに律速されうる。
判断基準
- 自分のアプリや検証コードで Google favicon API を呼ぶなら、裸ドメインではなく scheme 付きの URL を渡す。具体的には domain_url パラメータ、または domain に scheme 付き URL を渡せる経路を使い、HTTPS canonical の favicon キャッシュを見る。
- 裸ドメインの結果が古い/デフォルトアイコンでも、scheme 付き HTTPS URL の結果が正しいなら、配信ファイル自体は採用可能で、問題は外部キャッシュの URL キー差分(http キーの未反映)として切り分ける。
- 裸ドメイン表示まで揃えたい場合は、HTTPS だけでなく http スキーム側の URL(ルートと favicon パス)が、favicon 専用クローラに発見・取得される状態になっているかを確認する。https へ恒久リダイレクトしていても、リダイレクト元の http URL 自体が一度クロールされないと http キーのキャッシュは更新されない。
- Search Console で再クロールを依頼する対象は canonical の HTTPS ホームページにするが、これはページのインデックス更新であって favicon 専用クローラの http キー再取得を早める手段ではない。favicon は別系統で遅延しうる(インデックスとクロール系統が分かれている既存知見を参照)。公式に即時反映させる手段はなく、http 側のクロール待ちになることがある。
検証
外部から通常の取得と Googlebot / Googlebot-Image 相当の取得を行い、http ホームページ・favicon・アイコン本体がすべて 1 回の恒久リダイレクトで HTTPS の正常応答へ到達するかを見る。次に Google favicon キャッシュで裸ドメイン指定(http キー)と scheme 付き HTTPS 指定のレスポンスを比べる。HTTPS 指定だけ正しい場合は、サイト配信ではなく Google 側 http キーの favicon キャッシュ(=http 側クロール待ち)として扱う。