デスクトップではちゃんと表示されているのに、Safariのタブバーを見ると白い四角いアイコンになっている……そんな経験ありませんか? コードレビューでよく見かけるミスのひとつが、faviconのlinkタグをbodyに書いてしまうというものです。ブラウザはこのルールにかなり厳格です。
症状:アイコンが表示されない、あるいはデフォルトのまま
linkタグを追加し、ファイルパスも確認して、キャッシュもクリアした。それでも、デフォルトの地球儀アイコンのままか、何も表示されない。DOMを確認してみると、タグが<body>要素の中に居座っています。それが原因です。
解決策:headに移動させる
HTMLファイルを開き、body内にあるfaviconのlinkタグをカットしてください。そして<head>要素内の任意の場所にペーストします。これだけです。ブラウザをリロードすれば、すぐにアイコンが表示されるはずです。
<!-- 間違い: ブラウザはこれを無視します -->
<body>
<link rel='icon' href='/favicon.ico'>
<h1>私のウェブサイト</h1>
</body>
<!-- 正しい: ここに配置します -->
<head>
<link rel='icon' href='/favicon.ico'>
</head>ブラウザがbody内のタグを無視する理由
HTMLの仕様では、<link>要素は<head>に属すると明確に定められています。モダンブラウザは不正なHTMLに対して非常に寛容ですが、body内にリソースリンクがあるとパーサーの挙動が予測不能になります。
パーサーがbodyの解析を止める
ブラウザのHTMLパーサーがbody内で<link>タグを見つけると、通常は強制的にheadに移動させます。しかし、JavaScriptフレームワークがページ読み込み後に動的にタグを注入している場合、ブラウザがアイコンの再取得を実行しないことがあります。タブが空白のままになってしまうのです。
Flash of Unstyled Content (FOUC)
faviconのlinkタグが後から解析されると、ブラウザはアイコンのダウンロードが完了する前にタブをレンダリングしてしまう可能性があります。アイコンが表示されるまでの間、一瞬空白のタブが点滅するような状態になります。見た目があまり良くありません。
実例:GitHubはどうしているか
GitHubのソースコードを見てみてください。アイション関連の記述はすべて厳密に<head>内にまとめられています。標準的なICOフォールバック、モダンブラウザ向けのSVG、そしてApple Touch Iconを提供しています。これらのタグをDOM全体に散りばめるようなことはしていません。厳密な配置がレンダリングの競合状態を防ぐことを理解しているからです。我々もそれに倣うべきです。
2026年に向けた予防策
フレームワークの開発者は、コンポーネントベースのレイアウトを使っているため、このミスを犯しがちです。こうしたミスを防ぐための方法をいくつか紹介します。
- フレームワークのmetadata APIを活用する: Next.js、Remix、Astroにはすべて専用のmetadataエクスポート機能があります。レイアウトコンポーネントに手動でタグを配置するのではなく、これらを使いましょう。App Routerでのアプローチについては、Next.js faviconガイドを参照してください。
- DOMを監査する: Mzu favicondlを使ってサイトを検証しましょう。参考となる実装が必要な場合は、主要なサイトからfaviconをダウンロードして、headの構造がどのようになっているかを確認できます。
- 静的アイコンへのReact Helmetの使用をやめる: 2026年において、JavaScript経由で静的なfaviconタグを注入するのはアンチパターンです。headドキュメント内に生のHTMLとして記述しましょう。
アイコンはあるべき場所に配置しましょう。headはメタデータ用、bodyはコンテンツ用です。この境界を守れば、ブラウザのタブは常に完璧にレンダリングされます。