PageSpeed改善で、ブログ画像の役割を分けることにした
読了 約10分What's in this posts
点数は出たけど、何が起きたのかわからなかった
PageSpeed Insights は、GoogleがWebページの読み込み速度を100点満点で評価してくれる無料ツールです。スマホでの表示速度(モバイル)とパソコンでの表示速度(デスクトップ)を別々に測定してくれます。
昔々、ブックマークに入れていたものを掘り起こしたわけですね。
このブログのモバイル版を測定したら、こんな数字が出ました。
- Performance: 67(100点満点。低いほど遅い)
- LCP: 175.2秒
- Speed Index: 5.5秒
- Total network payload: 39,910 KiB(約39MB)
LCP(Largest Contentful Paint)は、ページを開いてから「一番大きなコンテンツ」が表示されるまでの時間です。175秒というのは、ほぼ「ページが完成しない」という意味になります。
パフォーマンス67という点数は見れば悪いとわかりますが、最初は何を直せばいいのかよくわかりませんでした。
調べてみると、原因はサーバーの遅さでも、JavaScriptのブロックでもありませんでした。トップページが大量の画像を読み込んでいたのが問題でした。
トップページが約39MBあった
サーバー応答は速かったです。JavaScriptのブロックも致命的ではありませんでした。大きかったのは、ひたすら画像でした。
トップページには最新記事のカードが並んでいます。各カードには記事のアイキャッチ画像が表示されます。見た目は小さなサムネイルですが、裏では5MB前後のPNGファイルをそのまま読み込んでいました。カードが10枚あれば、それだけで50MB近くになります。
小さく表示しているから軽い、とは限らない。
ブラウザは width を小さく指定されても、画像ファイルのサイズは変わりません。表示を縮小しているだけで、5MBのPNGは5MBのまま読み込まれます。
まず背景画像を軽くした
最初に手をつけたのは、ヒーロー背景画像でした。
ヘッダーに使っている hero-background.jpg が約7.6〜7.9MBありました。見た目はきれいですが、7MBは明らかに過剰です。
これをWebP(ウェブピー)形式に変換しました。WebPはGoogleが開発した画像フォーマットで、JPEGやPNGより同じ見た目で大幅にファイルサイズを小さくできます。変換後は約283KBになりました。同じ見た目で、ファイルサイズが約1/27になる計算です。
background-image: url('/images/hero-background.webp');
背景画像の変換を含むいくつかの修正をまとめてデプロイしたあと、この段階で計測し直しました。
- Performance: 67 → 75
- LCP: 175.2秒 → 5.0秒
- Total network payload: 39,910 KiB → 32,419 KiB
LCPは大きく改善しました。ただ、ペイロード(ページ読み込みに必要なデータの総量)はまだ32MBあります。トップページの記事カード画像がほぼそのまま残っていたからです。
lazy loadingだけでは足りなかった
背景画像の変換と並行して、記事カードのテンプレートに loading="lazy" と decoding="async" も加えました。
<img
src="{{ .Permalink }}"
loading="lazy"
decoding="async"
width="760"
height="424"
/>
それぞれの意味はこうです。
loading="lazy"— 画面の外にある画像は、スクロールして近づくまで読み込まないdecoding="async"— 画像のデコード(展開処理)がページ描画をブロックしにくくなるwidth/height— ブラウザが先に画像の枠サイズを確保できるので、レイアウトのズレが減る
ただ、ここで気づいたことがあります。
読み込み方の改善と、画像ファイルそのものを軽くすることは別の話だった。
loading="lazy" は読み込みのタイミングを遅らせるだけで、ファイルサイズは変わりません。PageSpeedの「Total network payload」は「このページを見るために合計何MBのデータを受け取ったか」を測っています。5MBのPNGをlazyで読んでも、最終的に5MBは受け取ります。
画像の役割を3つに分けた
ここまでやって、問題の本質が見えてきました。featured_image(記事のメイン画像を指定するフィールド)という1つのフィールドが、複数の場所で使われていました。
- 記事ページで大きく表示される
- トップページの一覧カードでも同じ画像が表示される
- SNSシェア時のOGP画像(SNSカードのサムネイル)にもなる
記事ページで「大きく見せるための画像」と、一覧で「小さく見せるための画像」を同じファイルにしていました。5MBのPNGをどちらにも使っていたのが、ペイロードの重さの原因でした。
そこで、フロントマター(記事ファイルの先頭にある設定ブロック)に役割ごとのフィールドを分けることにしました。
featured_image: https://images.keyuki.net/uploads/2026/05/slug-eyecatch.png
thumbnail_image: https://images.keyuki.net/uploads/2026/05/slug-thumb.webp
images:
- https://images.keyuki.net/uploads/2026/05/slug-og.jpg
| フィールド | 役割 |
|---|---|
featured_image |
記事ページで大きく見せる画像 |
thumbnail_image |
トップページや一覧で小さく見せる画像 |
images |
SNSカード用の画像 |
PageSpeedに直接効くのは thumbnail_image です。WebP形式で、ファイルサイズを小さく抑えた画像を用意します。featured_image に5MBの高解像度画像を置いたままでも、一覧カードではその画像を読まないようにします。
見た目は同じ「記事の画像」でも、ページ内での役割は違う。大きく見せる画像と、一覧で小さく見せる画像を同じファイルにしていたのが重さの原因だった。
古い記事はどうなる?
全記事を一度に直せるわけではありません。そこで、テンプレートは thumbnail_image を優先しつつ、なければ featured_image を使うように組みました。thumbnail_image がある記事から順に軽くなり、ない記事は従来通り表示されます。
サムネイル導入後の計測結果
直近の記事にサムネイルを設定してデプロイしたあと、PageSpeedで再測定しました。
- Total network payload: 32,419 KiB → 636 KiB
- FCP(First Contentful Paint、最初のコンテンツが表示されるまでの時間): 1.0秒
- Speed Index: 2.3秒
PageSpeedのリソース一覧に表示されたサムネイルのサイズはこんな感じでした。
| ファイル | サイズ |
|---|---|
| m4-mac-mini-workstation-thumb.webp | 約36 KiB |
| nasa-clean-air-plants-thumb.webp | 約30 KiB |
| desktop-title-size-research-thumb.webp | 約25 KiB |
| analytics-clarity-cloudflare-thumb.webp | 約17 KiB |
| mobile-heading-size-research-thumb.webp | 約15 KiB |
| x-card-og-image-hugo-ananke-thumb.webp | 約14 KiB |
もともと5MB前後あったPNG画像が、一覧カードでは14〜36 KiBのWebPに置き換わりました。
サムネイルを入れたら、今度は縦長に見えた
thumbnail_image を追加したあと、トップページのカード画像が縦長に見える問題が起きました。
もともと object-fit: cover(画像をトリミングして枠に合わせるCSS指定)はしていましたが、カード画像の枠(コンテナ)の高さが画像のアスペクト比に引きずられていました。縦長の画像が入ると、カードの縦幅がそれに合わせて伸びてしまいます。
修正は、カードの枠側に aspect-ratio: 16 / 9 を固定することでした。
.summary-image-link {
aspect-ratio: 16 / 9; /* 枠を16:9に固定 */
overflow: hidden; /* はみ出た部分を隠す */
}
.summary-img {
display: block;
object-fit: cover; /* 画像をトリミングして枠に収める */
width: 100%;
height: 100%;
}
速度改善の副作用で見た目のバグが出た、という話です。性能改善と見た目の確認はセットでやる必要があります。
Clarityとコントラストも整えた
ついでに2つ対応しました。
Microsoft Clarity の遅延読み込み
アクセス解析に使っている Clarity は、最初のページ描画には不要なスクリプトです。requestIdleCallback(ブラウザが手の空いたタイミングを通知する仕組み)を使って、描画が終わってから読み込むようにしました。対応していないブラウザは2秒後に読み込みます。
テキストのコントラスト改善
PageSpeedのAccessibilityスコア(アクセシビリティ:どれだけ読みやすいか)が91から96に上がりました。
- メタ情報(日付・著者など)の文字色:
#777→#666 - リンクの色:
#357edd→#1f5fbf - ホバー時:
#1753a8
Lighthouseのためだけでなく、モバイルでの読みやすさも上がりました。薄い背景に薄いグレーの文字は、屋外では特に見づらいです。
点数は少し、でも中身は大きく改善した
サムネイル導入後のPerformanceスコアは75から77に上がりました。
「2点しか上がっていない」と感じるかもしれません。正直、最初はそう思いました。
でも転送量は32MBから636 KiBまで下がっていました。FCPは1.0秒、Speed Indexは2.3秒まで改善しています。実際にスマホで見る人にとっては、かなり大きい改善です。
スコアが大きく動かなかった理由は、まだ残っている課題があるからです。
- LCPがまだ4.8秒 — ヒーローヘッダーの背景画像がLCP要素になっています
- TBT(Total Blocking Time)が280msに増加 — JavaScriptが長時間メインスレッドを占有している時間で、次の課題になりそうです
gtag/js(Google Analyticsのスクリプト)の未使用コード(約64 KiB)と Ananke テーマの未使用CSS(約12 KiB)が残っています
ただ、これらは当初39MBあった画像ペイロードに比べれば、はるかに小さい問題です。
デスクトップにも副次効果が出た
今回の修正はモバイルのPageSpeedを見て始めたものですが、一覧画像を軽くした効果はデスクトップにもそのまま出ました。
同じレポートのデスクトップ版の結果:
- Performance: 98
- LCP: 1.1秒
- FCP: 0.6秒
- Speed Index: 0.8秒
- Total network payload: 636 KiB(モバイルと同じ)
デスクトップも同じホームページ・同じ一覧カードを使っているので、サムネイルに切り替えた効果がそのまま反映されています。画像の役割分離は、画面サイズに関係なく効く改善でした。
今回わかったこと
- PageSpeedの数字は、分解すると意外と素朴な原因に戻る
loading="lazy"はタイミングの最適化であって、ファイルを軽くするわけではない- サムネイル用の画像を別に持つのは手間だが、一覧ページには効果が大きい
- 見た目の修正と速度改善は、片方だけ確認して終わりにしない
- 点数の伸びだけを見ると75→77で小さく見えるが、転送量は32MB→636 KiBまで下がった
- PageSpeedスコアと、実際にユーザーがダウンロードするデータ量は分けて見る必要がある
まとめ
今回のPageSpeed改善をまとめると:
- 背景画像をWebP化(7.6MB → 283KB)で LCP が 175秒 → 5秒に改善
- 一覧カードに lazy loading を追加したが、転送量自体は減らない
thumbnail_imageを導入して、一覧ページで5MBのPNGを読まないようにした → 総転送量が32MB → 636 KiBに- カード画像の縦長バグをCSS修正(aspect-ratio: 16/9)
- Clarityを遅延読み込みに変更
- テキストコントラストを改善してAccessibilityが91→96に
- モバイルはPerformance 77・LCP 4.8秒、デスクトップはPerformance 98・LCP 1.1秒
PageSpeedの改善というと難しそうに見えますが、今回やったことを一言で言えば「大きく見せる画像」と「小さく見せる画像」を同じものにしない、というだけでした。 前回に引き続き、こんなこと考えなくてもいいSaaSのありがたみを再認識しました。 でもまあ、学びもありましたね。