ウェブ画像最適化と聞くと、いまなら多くの人が、JPEGやPNGをAVIFやWebPなどの次世代画像フォーマットに変換することを思い浮かべるだろう。もちろんそれも有効だが、それは画像最適化の一部でしかない。
本当のウェブ画像最適化とは、画像ファイル単体ではなく、画像そのものと、画像を届ける仕組みの両面を最適化することだ。どれだけ画像を軽くしても、たとえばスマホに超高解像度の画像を配信したり、帯域幅の狭いVPSなどから配信していては、体感速度は改善しない。
CDNもただ使えばいいというものではない。アクセス数が多いサイトなのにキャッシュがほぼ効いていない状態では、画像の配信効率は悪いままだ。
Web画像最適化チェックリスト
早速だが、Web画像最適化のチェックリストは以下のとおりだ。
1. 適切な解像度で配信する
過剰な解像度で配信しない
ブラウザが実際に必要とするピクセル数は「CSS上の表示サイズ × DPR(デバイスピクセル比)」だ。Retinaディスプレイをはじめ DPR2〜3 の端末が一般的になり、私たちの目もそれに慣れたため、等倍(1:1)の画像はもう粗く感じる。表示幅の2〜3倍までは高精細化の効果があるが、それを超えると人間の目には判別できない過剰な解像度で、転送量を無駄にするだけだ。
さらにモバイルのように性能の低い端末では、大きすぎる画像はデコード処理の負荷が増え、動作が重くなる原因にもなる。
対応は、レイアウト上の最大表示幅 × 目標DPR(上限3程度)で必要なピクセル幅を割り出し、その幅で生成・配信すること。あわせて width/height 属性(または aspect-ratio)を付けてCLSも防ぐ。
srcset / sizes で出し分ける
単一解像度の画像をすべての端末に配信すると、どちらかのミスマッチが生じる。モバイルにデスクトップ用の巨大画像が届いてしまうか、逆に、固定した小さい画像では高DPR端末で解像度が足りずボケてしまうか、のどちらかだ。srcset(幅記述子つきの候補群)と sizes(レイアウト上の表示幅の宣言)を渡すと、ブラウザがビューポートとDPRを見て最適な1枚を選ぶ。
<img
srcset="photo-320.webp 320w,
photo-640.webp 640w,
photo-1280.webp 1280w"
sizes="(max-width: 600px) 100vw, 600px"
width="1280" height="853"
alt="...">sizes は「このブレークポイントではこの幅で表示する」というレイアウト情報なので、CSSと整合させるのが肝心だ。画角そのものを変えるアートディレクションが必要なら picture + source を使う。
ここで重要なのは、レスポンシブ対応の画像の出し分けを、CSSの表示/非表示(display:none やメディアクエリでの切り替え)でやらないことだ。CSSで隠しても画像自体は読み込まれてしまい、PC用とスマホ用の両方をダウンロードする、といった無駄が起きがちだ。端末ごとの出し分けは、CSSではなく srcset / sizes / picture に任せるのが正解だ(「5. 必要な画像だけ読み込む」でも触れる)。
2. 適切な画像形式を選ぶ
写真にロスレス(PNG)を使わない
PNGは可逆圧縮で、写真のような連続階調・高エントロピーの画像とは相性が悪く、同じ見た目でもJPEG/WebPの数倍〜10倍のサイズになる。「劣化させたくない」という理由でスクリーンショットや写真をPNGにしているケースは多いが、たいていは非可逆で十分だ。写真は原則JPEG/WebP/AVIF。透過が必要でPNGを選びがちな場面も、WebP/AVIFなら非可逆+アルファチャンネルが使えるので、透過付きの写真もPNGより大幅に軽くできる。
画像にしなくて済むものは画像にしない
ロゴ・アイコン・幾何学的な図はベクター(SVG)が適する。ラスタのロゴは高DPRでぼやけ、複数解像度を用意する手間もかかるが、SVGなら1ファイルで無限に鮮明、かつ多くの場合より軽量だ。
同じ発想で、そもそも画像にする必要がないものを画像にしないことも大切だ。文字はHTMLテキストとWebフォントで置けば、圧縮で潰れることも、SEOや多言語化で不利になることもない。単色の背景・グラデーション・角丸・影・境界線などもCSSで表現できる。たとえば「背景写真+その上のHTMLテキスト」に分ければ、背景は大胆に圧縮でき、文字はくっきり保てる。SVGも、SVGOにかけて不要なメタデータや小数桁を落とせばさらに小さくできる。
アニメーションGIFをやめる
GIFは256色制限・フレーム間圧縮が貧弱で、数秒のクリップでも数MBに膨れる。実写やグラデーションを含むループはとくに非効率だ。
対応は、実質「動画」であれば <video> で MP4(H.264)や WebM(VP9/AV1)に。短いUIループなら Animated WebP でもGIFから桁違いに小さくなり、対応ブラウザも広く実用的だ。自動再生ループにするなら muted playsinline loop を忘れずに。
なお Animated AVIF も規格上は可能で、使える環境では最も軽くできる可能性があるが、ブラウザ対応にばらつきがあり、エンコードや配信まわりのツールもまだ枯れていない。現時点では「余力があれば試す発展途上の選択肢」と捉え、まずは動画化か Animated WebP を軸にするのが無難だ。
3. 画像データを最適化する
画像の内容に合わせて圧縮品質・色数を選ぶ
JPEGは、描かれている内容によって「どこまで圧縮に耐えられるか」が大きく変わる。 細部やノイズの多い写真は劣化が目立ちにくく強く圧縮できるが、平坦な面やなだらかなグラデーションを含む画像は、同じ品質値でもブロックノイズやバンディング(縞)が出やすくなる。
つまり、サイト全体を一律の品質値(たとえば全部 q80)で書き出すと、画像ごとに「まだ削れるのに削っていない」「削りすぎて汚い」というムラが必ず出る。PNGも同様で、写真ではなくイラストやスクリーンショットなら、フルカラー(24/32bit)ではなく256色以下のパレットに落とせるものが多い一方、無闇に減色すると階調が破綻する。本来は画像1枚ごとに、内容を見ながら適切な品質・色数を選ぶべきなのだ。
問題は、それを人間が目視で1枚ずつ判断するのは、画像点数の多い普通のサイトでは現実的でないことだ。ここを自動化して1枚ずつ最適な設定を選ぶには、弊社が提供する LightFile Next のような、画像の特性を分析して最適な設定を自動選択する商用の画像最適化サービスを使うのが実務的な解になる。
最適化エンコーダを使う
同じフォーマット・同じ品質でも、エンコーダによってサイズは変わる。標準ライブラリの素朴なエンコードは非効率なことが多い。JPEGは MozJPEG(トレリス量子化で同画質でも小さい)で再エンコード、PNGは pngquant(減色・パレット化)や oxipng / zopflipng(可逆再圧縮)で詰める。これらは手作業ではなく、ビルドパイプラインやアップロード処理に組み込むのが現実的だ。
不要なメタデータを除去する(ただしOrientation・ICCは要注意)
写真には EXIF / IPTC / XMP、撮影機材の情報、埋め込みサムネイル、編集ソフトの作業データなどが付き、数十KB単位で無駄になることがある。これらはWeb配信には不要だが、exiftool -all= のような一括削除は事故のもとで、次の2つは要注意だ。
- Orientation(回転情報):消すと、EXIFの回転を前提にした画像が横倒し・上下逆で表示されることがある。削除するなら、先に回転をピクセルへ焼き込む(auto-orient)。
- ICCカラープロファイル:消すと、広色域や非sRGBの画像の色がくすんだり転んだりする。保持するか、sRGBへ変換してから外す。
要は「表示に無関係なデータだけ外し、表示に影響するものは残すか、焼き込む・変換してから外す」というのが安全な進め方だ。
次世代フォーマットをフォールバック付きで採用する
Web画像の代表フォーマットであるJPEG/PNG/GIFは登場から30年以上が経ち、圧縮効率に限界がある。
WebPやAVIFは、同等の画質を従来フォーマットの半分以下のファイルサイズで配信できる。弊社の実務経験でも、ほとんどの画像で半分以下にできている。加えて透過やアニメーションにも対応する。変換には cwebp(WebP)や avifenc(AVIF)などのエンコーダを使う。
フォーマットの出し分け(フォールバック)は、HTMLのマークアップで実現する方法もある。picture + source(type="image/avif" → type="image/webp" → 旧フォーマット)と並べれば、対応ブラウザだけが新フォーマットを読み込む。
ただ、圧倒的におすすめなのはサーバー側でフォールバックする方法だ。ブラウザは Accept ヘッダーにWebP/AVIFの対応状況を載せてリクエストしてくるので、サーバーはそれを見て、非対応の相手にだけ従来フォーマットを返せばよい。HTMLを書き換えず1つのURLで自動的に出し分けられるため、実務上は圧倒的に現実的だ。AVIFはエンコードが重い点だけ、運用で見込んでおく。
4. 画像配信を最適化する
ここからは「届ける仕組み」だ。画像が十分軽くても、届け方が悪いと「画像は軽いのに表示が遅い」状態になる。以下はいずれもサイト運営者が制御できる要因だ。
CDNを効果的に使う
画像を単一オリジン(例:東京のサーバー)だけに置くと、遠方のユーザーは毎回そこまで取りに行く。距離はRTTに直結し、TLSハンドシェイクも往復するため、遠いほどTTFBが積み上がる。CloudFront / Cloudflare / Fastly などで最寄りエッジから配信すれば、これを短縮できる。「海外サーバーだから遅い」と言われがちだが、本質は距離そのものではなくエッジ配信をしていないことだ。
ただし、CDNを入れれば万全というわけではない。 CDNは経路に一段はさむ仕組みなので、キャッシュヒット率が低ければ、エッジで空振りしてオリジンまで取りに行く「寄り道」が増えるだけになりかねない。効果はキャッシュが効いてこそだ。逆に、更新頻度が低く規模の小さいサイトなら、素直にオリジンから配信したほうが単純で速いこともある。要は「CDNを使うか」ではなく「CDNを効果的に使えているか(キャッシュ設計とセットになっているか)」が問われる。
無造作な同期変換(オンザフライ変換)を見直す
オリジンが画像を返すのに時間がかかると、それはそのままTTFBに乗る。とくに見落としやすいのが、リクエストのたびに画像をリサイズ・フォーマット変換する「同期的なオンザフライ変換」だ。変換処理そのものがレスポンスをブロックするため、100KBの画像でもTTFBが数百msに達することがある。静的ファイルなら本来、数十msで返せるはずだ。
対応は、変換を事前生成して静的ファイル化するか、変換結果をキャッシュして2回目以降は再変換しないこと。つまり変換を、リクエスト経路の同期処理から外す。弊社の LightFile Proxy は、初回は元画像を短時間キャッシュで即座に返しつつ裏側でWebP変換を進め、変換が済んだ以降のリクエストから変換済み画像を長時間キャッシュで配信する。同期的なオンザフライ変換のTTFBペナルティを、利用者に負わせない設計だ。
帯域を広く取り、輻輳を避ける
100Mbps級のVPSにアクセスが集中すると、上限帯域で頭打ちになり、全ユーザーのダウンロードが遅くなる。大きなヒーロー画像ほど帯域を食い、輻輳を招く。クラウドやCDNでは起きにくい一方、自社サーバーやVPSからの直配信では今も現実的な問題だ。対応は、画像だけでもCDNへ逃がして配信をオフロードすること、そして1〜3で画像の総転送量そのものを減らすこと。軽い画像は帯域の問題の予防にもなる。
正しいキャッシュ戦略をとる
Cache-Control: no-cache や極端に短い max-age になっていると、変わらないロゴでも毎回サーバーに問い合わせ、そのたびRTTが発生する。20KBの画像でもTTFB分は毎回かかり、逆に1MBでもブラウザのローカルキャッシュが効けば、2回目以降はネットワークに出ず実質0秒だ。「更新できなくなるのが怖くて長くできない」という理由で短くしているサイトも多い。
定石は、長期の max-age(例 max-age=31536000)とキャッシュバスターの組み合わせだ。ファイル名にコンテンツハッシュを含める(例 logo.a1b2c3.webp)か、クエリにバージョンを付ける(例 logo.webp?v=a1b2c3)ことで、中身が変われば別URLになり、長期キャッシュと確実な更新を両立できる。あわせて immutable を付ければ、有効期間中の再検証も省ける。頻繁に変わる動的画像は ETag / Last-Modified で条件付きリクエストにする。
補足:レイテンシとスループット、HTTP/2・HTTP/3 「画像が遅い」の内訳は画像サイズで変わる。小さな画像(数十〜数百KB)では接続確立やTTFBなどレイテンシが支配的になりやすく、大きなヒーロー画像では転送速度(スループット)も効く。上のCDN・オリジン性能・帯域・キャッシュは、この両方に効く施策だ。なおHTTP/2・HTTP/3も多数画像の並列取得に有効だが、いまはほとんどのCDN・Webサーバーで既定なので、モダンな構成なら特に意識しなくて構わない。
5. 必要な画像だけ読み込む
JavaScriptによる遅延読み込みをやめる
かつては遅延読み込みをJavaScript(data-src を IntersectionObserver で差し替える等)で実装するのが一般的だったが、いまは不要だ。JS方式は、スクリプトの読み込みと実行を待ってから画像取得が始まるため、かえって表示が遅れたり、JSが失敗すると画像が出ないといった脆さがある。標準の loading="lazy" に置き換えたい。ブラウザネイティブなので速くて確実だ。
遅延読み込みを正しく使う
loading="lazy" は、ページ下部(初期表示に不要な画像)に対して使うのが基本だ。初期表示外の画像を最初から全部取得すると、初期の帯域と接続を奪い合ってしまう。
ただし逆に、ファーストビュー内、とくにLCP候補の画像に lazy を付けてはいけない。 遅延によって読み込みが後ろにずれ、LCPがかえって悪化する。lazyはページ下部限定、が鉄則だ。
CSSで画像を出し分けない
「1. 適切な解像度で配信する」でも触れたが、レスポンシブ対応でCSSのメディアクエリと display:none で画像を出し分けるのは避ける。CSSで隠しても画像自体はダウンロードされるため、PC用・スマホ用の両方を読み込む無駄が生じる。端末ごとの出し分けは srcset / sizes / picture に任せ、実際に必要な1枚だけを読み込ませるのが正解だ。
優先度を制御する
ブラウザの既定の優先度では、ページ上部の主役画像が他リソースに埋もれて出遅れることがある。LCP画像には fetchpriority="high" を付けて「これは重要だ」と明示したい。ただし土台になるのは、あくまでその画像から lazy を外しておくことだ。
一方、<link rel="preload"> は、素の <img src> で書かれたLCP画像には基本的に不要だ。プリロードスキャナーがそのまま画像を発見して早期に読み込むうえ、preloadを多用するとかえって優先度のヒントが薄まり、本当に優先したいリソースが埋もれる。preloadが効くのは、CSS背景画像やJavaScriptで後から挿入される「発見されにくい画像」に限られる。詳しくは「LCP画像は最優先で読み込ませる」で掘り下げている。
まとめ
Web画像最適化は「AVIF/WebPへの変換」という単一作業ではない。画像そのもの(①解像度 ②形式 ③データ)と、届ける仕組み・読み込み方(④配信 ⑤読み込み)の5本柱を横断して初めて、本当の意味での最適化になる。まずはチェックリストで自社サイトを一通りチェックしてみてほしい。
そして③で触れたように、画像1枚ごとに最適な圧縮を施す作業は、本来は手間がかかり自動化も難しい領域だ。ここを効率化したいときは、弊社の LightFile Next(画像を1枚ずつ分析して最適化)や LightFile Proxy(CloudFront向けの自動WebP変換)も検討してほしい。