2025/02/26

テクノロジー

Next.jsの画像最適化の落とし穴

この記事の目次

    マイナビジョブサーチ WebチームのK.Kです。
    今回は、Next.jsの画像最適化のお話をしたいと思います。

    先日インフラ担当の方からこんな指摘をされました。
    「Acceptヘッダーを転送していないから、画像の最適化が活かされていない。」
    最初よくわからなくて「?」となったので、これをきっかけにNext.jsの画像最適化について調べてみました。

    Next.jsの画像最適化とは?

    Next.jsではimgタグ、または、next/imageのImageコンポーネントを使用して画像を描画します。

    この内、next/imageを使用すると、画像の最適化を行うことができます。
    「リサイズ」「フォーマット変換」などを行うことで、ページ読み込み速度が向上するなどといった恩恵が得られます。

    リサイズ

    リサイズの機能では、表示するディスプレイの大きさに合わせて、画像のサイズを最適化してくれます。

    例えばジョブサーチ上のこの画像、以下のように記載されています。

    srcset属性に複数のURLが並んでいることがわかります。
    これらが、画面サイズごとの画像のパスを示しています。

    PC
    https://jobsearch.mynavi.jp/_next/image?url=%2Fapi%2FserveImage%3Furl%3Dhttps%253A%252F%252Fmynavi-agent.jp%252Fjobsearch%252Fimg%252Fjobimg%252Fjob1_01%252F02.webp&w=3840&q=75

    SP(よりさらに少し狭めた場合)
    https://jobsearch.mynavi.jp/_next/image?url=%2Fapi%2FserveImage%3Furl%3Dhttps%253A%252F%252Fmynavi-agent.jp%252Fjobsearch%252Fimg%252Fjobimg%252Fjob1_01%252F02.webp&w=640&q=75

    PC だと「690 × 398」SPだと「640 × 369」の画像が表示に使用されます。
    容量を比べると23KB vs 18KBと、SPの方が小さくなっています。
    特にSPは通信環境によってページ表示が遅くなりがちなので、容量が小さくなるのは嬉しいですね!
    元画像がもっと大きい場合は、より大きな差になるかと思います。

    画面サイズごとの画像のパスは、next/imageを使用すると、自動で生成されます。
    これらの中から最適な画像をブラウザ上に表示してくれます。

    フォーマット変換

    フォーマット変換では、ブラウザが対応している画像フォーマットに合わせて、画像フォーマットを最適化してくれます。

    どういったフォーマットに変換するかは設定で変更することができ、デフォルトではWebPに変換される設定になっています。

    module.exports = {
      images: {
        formats: ['image/webp'],
      },
    }

    WebPは、JPEGやPNGより圧縮率の高いフォーマットです。
    他にAVIFも指定できますが、こちらはより圧縮率が高い代わりに、エンコードに時間がかかるようです。

    さて、ジョブサーチのこちらの画像を取り上げます。

    この画像、元はJPEGとして保存されています。
    これをジョブサーチ上で開くと、JPEGで表示されます。
    WebPじゃないの?
    Chromeの最新版だから対応しているはずだけど?

    そう、これが冒頭で書いた、今回指摘された内容です。

    「ブラウザが対応している画像フォーマットに合わせて」画像フォーマットを最適化
    ブラウザが対応している画像フォーマット、これは何で判断しているのでしょうか?
    それが、Acceptヘッダーなんです。

    ↓ Acceptヘッダーとは?

    HTTP の Accept リクエストヘッダーは、クライアントが理解できるコンテンツタイプを MIME タイプで伝えます。

    実際にChromeでリクエストヘッダーのAcceptを見ると「image/webp」の記載があります。
    Next.jsではこの値を見て、WebPで返すかどうかを判断しているわけですね。

    公式ドキュメントにも以下の記載がありました。

    Good to know:
    ~ 省略 ~
    If you self-host with a Proxy/CDN in front of Next.js, you must configure the Proxy to forward the Accept header.

    参照元:https://nextjs.org/docs/app/api-reference/components/image#formats

    ジョブサーチでは、Next.jsの動作環境にAcceptヘッダーを転送していなかったため、WebPのブラウザがフォーマットに対応しているか判定できず、そのままのJPEGフォーマットで返していたようです。

    それでは、実際にAcceptヘッダーを転送して、フォーマットの変換をするとどうなるのか、検証環境で動作させてみました。
    すると、

    • 元画像(JPEG, 2000×1333):120KB
    • Acceptヘッダーなし(JPEG, 916×611):41KB
    • Acceptヘッダーあり(WebP, 916×611):20KB

    ということで、画像の容量が半分になりました!(圧縮されすぎでは?)
    見た目上は変化がわからないのに、これはすごいですね!

    最後に

    next/imageで画像の最適化をすることで、ページの読み込みが早くなったり、他にも視覚的な安定性が得られたりといったメリットがあります。
    SEOにおいてもメリットがあるので、せっかくNext.jsを使用しているなら、動作しているのかちゃんと確認しないとなと思いました。

    参考

    https://nextjs.org/docs/app/building-your-application/optimizing/images
    https://nextjs.org/docs/app/api-reference/components/image#formats
    https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Accept

    ※本記事は2025年02月時点の情報です。

    著者:マイナビエンジニアブログ編集部