WordPressサイトのページ表示速度を落としている最大の原因は、多くの場合コードではなく画像だという調査結果がよく引用される。同じ見た目の写真でも、JPEGやPNGのままアップロードするか、WebPに変換するかでファイルサイズが数十%変わることがある。今回は、WebPがなぜ軽いのか、ブラウザはどうやって対応形式を判断しているのか、そしてWordPress自身がこの変換をどう扱っているかを整理する。
WebPが軽い理由 — 圧縮アルゴリズムの違い
JPEGは1992年に標準化された画像圧縮方式で、離散コサイン変換(DCT)をベースにしている。対してWebPはGoogleが2010年に発表した比較的新しい形式で、動画コーデックVP8のイントラフレーム圧縮技術を静止画向けに転用している。動画用に磨かれた予測符号化の技術を流用しているぶん、同程度の見た目の劣化(画質)を保ったまま、JPEGよりも小さいファイルサイズに収まりやすい。
補足: イントラフレーム圧縮とは、動画の中の1コマ(フレーム)を、前後のコマを参照せずにそのフレーム単体で圧縮する方式のこと。静止画1枚の圧縮とほぼ同じ問題であるため、動画コーデックの技術を静止画フォーマットに転用しやすい。
WebPのもう1つの利点は、1つのフォーマットで複数のユースケースをカバーできる点だ。写真のような不可逆圧縮(ロッシー)だけでなく、ロゴやスクリーンショットのような可逆圧縮(ロスレス)も同じ拡張子でサポートしており、PNGのような透過(アルファチャンネル)も、GIFのようなアニメーションも扱える。つまり「写真はJPEG、透過画像はPNG、簡単なアニメーションはGIF」という従来の使い分けを、WebP 1つでまとめられる設計になっている。
ブラウザはどうやって対応形式を判断しているか
WebPは登場当初、対応ブラウザが限られていた。この過渡期を支えたのが、HTTPの「コンテントネゴシエーション」という仕組みだ。ブラウザは画像をリクエストする際、Accept: image/webp,image/*,*/* のようなヘッダーを送信しており、サーバー側やCDNはこのヘッダーを見て「このブラウザはWebPを解釈できる」と判断し、対応していれば軽いWebP版を、対応していなければ従来のJPEG/PNGを返す、という出し分けができる。
補足: コンテントネゴシエーションとは、同じURLに対して、クライアントの申告する対応フォーマットに応じてサーバーが違う中身を返す仕組みの総称。画像形式だけでなく、言語(
Accept-Language)や圧縮方式(Accept-Encoding)でも同じ考え方が使われている。
HTML側でも同様の考え方を明示的に書ける。<picture> 要素の中に <source type="image/webp" srcset="photo.webp"> を書き、その後ろに通常の <img src="photo.jpg"> を置いておくと、ブラウザは自分が解釈できる形式を上から順に探し、対応していれば .webp を、対応していなければ <img> の .jpg にフォールバックする。サーバー側の判定ロジックを書かなくても、ブラウザ自身が「自分に合う形式」を選んでくれる点が実装として扱いやすい。
現在の主要ブラウザ(Chrome・Firefox・Edge・Safari)はいずれもWebPに対応済みのため、この判定ロジックが実際に「非対応」の枝を通ることは少なくなってきているが、古いメールクライアントのプレビューエンジンや一部の画像処理ボット等、非対応の閲覧環境が完全にゼロというわけではないため、フォールバックを残しておく設計は今も有効だ。
WordPress本体でのWebP対応
WordPressは長らく、アップロード可能な画像形式をJPEG・PNG・GIF等に限定しており、WebPファイルをメディアライブラリに直接アップロードしようとすると「セキュリティ上の理由でアップロードが許可されていないファイル形式です」というエラーになる時期があった。WebPが wp_get_mime_types() の許可リストに正式に加わったのはWordPress 5.8からで、これ以降はメディアライブラリに直接WebPファイルをアップロードできるようになっている。
さらに新しいバージョンのWordPressでは、アップロードされたJPEG画像から生成されるサムネイル等の派生サイズ(add_image_size() で登録された各サイズ)を、自動的にWebPに変換して保存する機能も追加されている。元のJPEGファイル自体は変更せず、記事一覧やアイキャッチ表示に使う縮小版だけをWebPにすることで、表示側の転送量を減らしつつ、元データの互換性は保つという設計になっている。この変換処理はサーバー側の画像処理ライブラリ(GDまたはImagick)がWebP出力に対応しているかどうかに依存するため、レンタルサーバーの環境によっては自動変換が効かないこともある。
自社実装での類似ケース — 拡張子とMIMEタイプの対応表
本アプリのホワイトラベルレポート機能(STD/BIZプラン向け)にも、WebPを含む画像形式を扱う処理がある。エージェンシーロゴのアップロードAPI(site_manager_web.py::upload_report_logo)は、許可する拡張子の一覧に .webp を含めており、アップロードされたロゴをプレビュー表示するAPI(report_logo_preview)では、拡張子から image/webp というMIMEタイプを引く対応表を持っている。
mime_map = {'.png': 'image/png', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg',
'.gif': 'image/gif', '.webp': 'image/webp', '.svg': 'image/svg+xml'}
同様に、レポートPDF内にサイトのスクリーンショットを埋め込む処理(report_generator.py::_encode_image_b64)でも、画像ファイルをBase64のdata URIに変換する際、拡張子からMIMEタイプ(webp なら image/webp)を判定してから埋め込んでいる。
なぜこの対応表がわざわざ必要なのか。ブラウザやPDFレンダラーは、拡張子ではなく実際に送出された Content-Type ヘッダー(またはdata URI内のMIME指定)を見て画像を解釈する。ここで誤って .webp ファイルに image/png のようなMIMEタイプを付けてしまうと、ブラウザが画像として解釈できずに読み込みエラーになったり、インライン表示せずダウンロードダイアログを出してしまったりすることがある。拡張子ごとに正しいMIMEタイプを明示的に対応させておくことは、地味だが表示崩れを防ぐ基本的な工程だ。
なぜOGP画像はWebPではなくPNGのままなのか
一方で、本ブログのOGP画像(記事一覧やSNSでシェアされたときに表示されるカード画像)を動的生成している ogp-generator.php(Cache-ControlとETagの記事で扱った題材)は、あえてWebPではなくPNGで出力している。理由は、X(旧Twitter)やFacebook等のSNSクローラは、ブラウザとは別の独立したプログラムであり、必ずしも最新ブラウザと同じ画像形式サポート状況になっているとは限らないためだ。OGP画像は「人間のブラウザ」ではなく「SNS側のクローラ」に読み込ませることが目的であるため、対応形式が最も枯れているPNG/JPEGを選んでおく方が、カードが正しく表示されないという事故を避けやすい。
同じ「画像を軽くしたい」という目的でも、表示先が一般ブラウザなのかSNSクローラなのかによって、選ぶべきフォーマットの判断が変わってくる例といえる。
まとめ
WebPは動画コーデックの圧縮技術を静止画に転用することで、透過やアニメーションも含めて1フォーマットでJPEG・PNG・GIFより軽いファイルサイズを実現している。ブラウザ側は Accept ヘッダーや <picture> 要素を通じて対応形式を申告・選択でき、WordPress本体もバージョン5.8以降でアップロード対応、その後のバージョンで自動変換対応と、段階的にWebPを取り込んできた。ただし「軽ければ常に正解」というわけではなく、表示先の互換性(SNSクローラ等)によっては、あえて枯れた形式を選ぶ判断も必要になる。