紡Book の勉強ノートに戻る
コードレビュー学習2026.05.09

とりあえず大きい画像を置いておけば綺麗、という話じゃなかった — Next.js の icon convention

Next.jsfaviconperformanceWeb Vitals

コードレビューで見つかった問題を、勉強のためにまとめたものです(修正済み)。

この記事でわかること

  • Next.js の app/icon.png を置くだけで favicon になる仕組み
  • そこに大きな画像を置いてしまうと何が起きるか
  • どう直したか
  • 「便利な convention は、素のまま配る」を意識するための学び

Next.js の icon convention は素通しで配る

Next.js (App Router) には、app/icon.png というファイル名でファイルを置くだけで、それを favicon として配ってくれる convention があります。 具体的には、ビルド時に Next.js が以下の HTML を自動で <head> に挿入してくれます:

html
<link rel="icon" type="image/png" sizes="2048x2048" href="/icon?[hash]" />
実体はリポのファイル。DB にもメモリにも無い。ディスク上のファイルfrontend/src/app/icon.pngリポに保存されている PNG閉じても残る変換なし静的配信 /icon同じバイトリサイズなし圧縮なしビルド成果物ブラウザfavicon として読む置いたサイズのまま全ページで取りに来るキャッシュまで毎回画像はディスク上の icon.png。convention は関所で、置き場所ではない。通したバイトが右のブラウザへ届く。途中で小さくならない。

ここがポイントで、app/icon.png に 6.7MB の PNG を置けば、ユーザは 6.7MB の PNG を favicon として読みに来ます。

修正前のコード

app/icon.png に置いていたファイルの実体:

text
$ file frontend/src/app/icon.png
frontend/src/app/icon.png: PNG image data, 2048 x 2048, 8-bit/color RGBA, non-interlaced
 
$ ls -la frontend/src/app/icon.png
-rw-r--r--  1 user user 6884468 Apr 30 23:23 frontend/src/app/icon.png

2048×2048、約 6.7MB。 これが Next.js の icon convention によって、サイト全ページの <link rel="icon"> から参照される状態になっていた。

なぜ危険か

ふだんブログを読んでいて「favicon が遅い」と感じる場面はほとんどありません。なぜなら 普通の favicon は数 KB ですから。今回のように 6.7MB あると、次のことが起きます。

  • 初回訪問のあらゆるページで 6.7MB を追加ダウンロードします
    • ランディングページに 1MB の画像を必死で詰めていても、favicon で 6.7MB 配ったらすべて意味がなくなります
  • ブラウザのキャッシュに乗るまでの体感が悪化します
    • favicon は HTML のパース後に並列で取りに行くため、メインコンテンツの表示はブロックしません
    • ただし、回線が細い環境(モバイル / 公共 Wi-Fi)では、メインコンテンツのリソース取得と帯域を奪い合います
  • CDN コスト・転送量がじわじわ効いてきます
    • 大量に bot が巡回するサイトだと、favicon だけで月のトラフィックが跳ね上がります
  • 検索エンジンの Web Vitals 評価が下がります
    • Largest Contentful Paint や Interaction to Next Paint といった指標は「画面に映るまでの体感」を測るので、帯域を食われると数値が悪化します

総じて「あらゆるページで配るアセット」の肥大化は、点ではなく面で効く タイプの問題になります。

対策(修正後の方針)

favicon としての役割に必要な解像度・サイズに揃えます。具体的には次のように分割します。

3つとも frontend/src/app/ のファイル。DB にもメモリにも無い。icon.png32×32 または 192×192約 30KB の PNGタブ・faviconconvention が拾うディスクに保存apple-icon.png180×18050KB 未満の PNGiOS のホーム追加convention が拾うディスクに保存opengraph-image.jpg1200×630 程度JPEGSNS のカードconvention が拾うディスクに保存ファイル名で行き先が決まる。同じロゴを1枚で兼ねない。OGP は favicon より大きくてよく、JPEG でもよい。サイズ基準が別物である。

ポイントは:

  • favicon は ブラウザに小さく表示される画像 なので、解像度を盛りません
  • 「サイトのロゴを大きく見せたい場面(OGP / ヒーロー画像)」と「favicon」を 同じ画像で兼ねません
  • 1ファイルに兼用させたくなったときは、いったん「ファイル名は別、画像は同じソースから書き出す」設計に倒します

学びポイント

  • 「全ページで読まれるアセット」(favicon、共通フォント、共通 JS)は、点で見ると小さくても面で効きます。サイズの上限を1つの数字で意識します(例: favicon < 30KB、共通 CSS < 100KB)
  • 便利な convention(ファイル名で配信される系)ほど、Next.js Image のように暗黙でリサイズしてくれる経路と混同しません
実体はどちらもリポのファイル。DB にもメモリにも無い。Image コンポーネントで読むディスクのソース画像リポに保存されている閉じても残るリサイズ・WebPブラウザへ小さい派生変換済みの小さい画像元ファイルとは別の配信icon / public/ 直下 / フォントディスクのソース画像リポに保存されている閉じても残る変換なし置いたバイトのままfavicon・public・フォント最適化レイヤーは無い最適化は上の経路にだけある。convention は関所で、置き場所ではない。下の経路では、ディスクのファイルがそのまま右へ届く。

おわりに

「LP のヒーロー画像をどう圧縮するか」みたいな話は、最近は Next.js Image が黙って WebP / AVIF にしてくれるので、ぼんやりしていても致命傷にはなりにくいです。

「便利な convention は、置いたファイルをそのまま素で配る」 ── このタイプのミスは、フレームワークが面倒を見てくれている範囲を勘違いしているとき に起きやすいです。Next.js Image / Server Components / convention based routing といった「賢いレイヤー」と、素通しのレイヤー が同じプロジェクトに同居していることを意識しておくと、似たミスを踏みにくくなります。


この記事は2026年5月時点の自分の理解で書いています。仕様や認識が変わってる可能性があります。