WordPressのCSS、JavaScript、フォントを調べ、1ページあたり427,918バイト減らしました。ところがPageSpeed Insightsの実ユーザーデータを見ると、LCP 2.9秒のうちTTFBが1.6秒。ページの中身を受け取る前の待ち時間が55%を占めていました。容量削減とは別に、CDNキャッシュを調べる必要がありました。
まず1ページあたり約418 KB減らした
2026年9月21日に行った軽量化は次の4項目です。
| 作業 | 削減量 |
|---|---|
子テーマ style.css の二重読み込み解消 | 13,324 B |
| テーマ書体・絵文字・jquery-migrateの停止 | 119,802 B |
| Envo Extraの停止 | 93,432 B |
| Font AwesomeのSVG置き換え | 201,360 B |
| 合計 | 427,918 B(約418 KB) |
CSSは27本から23本、JavaScriptは18本から16本、ローカルのwoff2フォントは5本から0本になりました。
これは無意味な削減ではありません。同じサーバーから、削減量とほぼ同じ436,628 Bの実ファイル6本をHTTP/2で並列取得すると、光回線でも0.38〜0.85秒かかりました。Lighthouseのモバイル設定に近い1.6 Mbpsなら、単純計算で約2.1秒です。
ただし、ここで「サイトが何秒速くなった」とは言えません。実際の表示時間には、サーバー応答、HTML解析、CSS、JavaScript、画像、端末性能などが重なっているからです。
実ユーザーLCP 2.9秒のうちTTFBが1.6秒
作業当日にPageSpeed Insightsで取得した、モバイルのフィールドデータは次のとおりでした。
| 指標 | 値 |
|---|---|
| LCP | 2.9秒 |
| FCP | 2.9秒 |
| TTFB | 1.6秒 |
| CLS | 0 |
TTFBは、ページへアクセスしてから最初の1バイトが届くまでの時間です。HTML、CSS、画像がどれだけ小さくても、その前に1.6秒待っていれば、後に続くFCPやLCPも早く始まりません。
web.devでは、多くのサイトが目安として0.8秒以下を目指すよう案内しています。1.6秒は「改善が必要」の帯です。TTFBはCore Web Vitalsそのものではありませんが、後続の指標すべての土台になります。
今回減らした418 KBは、最初の1バイトが到着した後の転送や描画に効くものです。TTFB 1.6秒を直接縮める作業ではありません。
手元ではTTFBが291ミリ秒だった
ところが、自宅の光回線から複数回測るとTTFBは194〜440ミリ秒で、中央値は291ミリ秒でした。PageSpeed Insightsの実ユーザーデータ1.6秒とは大きな差があります。
同じサイトなのに差が出る理由を調べると、レスポンスヘッダーに次の指定がありました。
cache-control: s-maxage=300
s-maxage はCDNなどの共有キャッシュに対する保持時間です。300秒なので、キャッシュは5分で古くなります。
さくらのレンタルサーバの公式サポートにも、コンテンツブーストの初期キャッシュ時間は s-maxage=300 とあります。僕のサイトで観測した値と一致しました。
キャッシュMISSはHITの約3倍かかった
同じ記事を続けて取得し、CDNの x-cache とTTFBを比べました。
| 記事 | x-cache | TTFB |
|---|---|---|
| 記事A・1回目 | MISS | 0.853秒 |
| 記事A・2回目 | HIT | 0.290秒 |
| 記事B・1回目 | MISS | 1.006秒 |
| 記事B・2回目 | HIT | 0.331秒 |
キャッシュが当たれば約0.3秒ですが、外れると0.85〜1.0秒でした。MISS時はCDNがオリジンのWordPressへ取りに行き、PHPなどの処理を待つためです。
このサイトには1,295本の記事があり、流入は検索が中心です。ある記事に5分以内に次のアクセスが来なければ、次もMISSになる可能性があります。
実際のMISS率をアクセスログで集計したわけではないため、「多くの訪問がMISSになっている」は現時点では推測です。ただし、5分という設定、MISSとHITの実測差、CrUXのTTFB 1.6秒は、同じ方向を示しています。
画像には private が付き、CDNに載っていなかった
さらに、HTML、CSS、JavaScriptと画像でキャッシュ指定が違うことも分かりました。
| 種類 | Cache-Control | 観測した動作 |
|---|---|---|
| HTML | s-maxage=300 | CDNで5分 |
| CSS / JS | max-age=604800, s-maxage=300 | ブラウザ7日、CDN5分 |
| 画像 | private, max-age=2592000 | CDNは毎回MISS |
HTTPキャッシュの仕様では、private が付いたレスポンスは共有キャッシュへ保存できません。実際、同じ画像を繰り返し取得しても x-cache: MISS、age: 0 のままでした。
一方、WebP変換自体は機能しています。同じURLへ Accept: image/webp を付けると、JPEG 94,380 BではなくWebP 69,938 Bが返り、約26%小さくなりました。
確認できている事実は、次の3点です。
Acceptに応じてJPEGとWebPを出し分けている- レスポンスには
Vary: AcceptとCache-Control: privateが付く - 画像はCDNで毎回MISSになる
WebP変換プラグインの出し分け処理が private の発生に関係している可能性は高いものの、原因プラグインはまだ特定できていません。ここは確定事項ではなく、次の調査対象です。
次にやることの順番が変わった
軽量化を始めたときは、ファイル容量を減らすことが中心でした。しかし実測後の優先順位は変わりました。
| 優先 | 施策 | 期待する効果 |
|---|---|---|
| 1 | 画像の private の原因特定 | 画像をCDNへ載せられる可能性 |
| 2 | s-maxage 延長の検討 | HTMLのHIT率を上げ、TTFBを短縮 |
| 3 | gzip圧縮の解決 | HTML 166 KBを約36 KBへ縮小する見込み |
| 4 | レンダリングブロックCSSの整理 | Lighthouseが示す待ち時間を短縮 |
| 5 | 不要アセット削減 | 427,918 B削減済み |
ただし、キャッシュ時間を長くすれば無条件に良いわけではありません。更新後の内容が古いまま残る時間も長くなります。画像の出し分けを残すなら、CDNが Vary: Accept を正しく扱えるかも確認が必要です。
設定を変更する前に、s-maxage=300 の出どころ、画像へ private を付ける処理、キャッシュ削除の方法を特定します。そのうえで、HIT/MISS、WebPの出し分け、更新内容の反映を再測定します。
容量と待ち時間は、別々に測る
427,918 B減らしたことは、転送量とファイル数を確実に減らしました。特にフォント181 KBはgzipでも減らせないため、読み込まないようにした意味があります。
一方、LCPの前半を占めていたTTFBは別の問題です。418 KBを削ったからといって、サーバーが最初の1バイトを返すまでの1.6秒が直接短くなるわけではありません。
今回の収穫は「高速化できた」と言い切れる数字より、次に調べる場所が分かったことでした。容量、リクエスト数、TTFB、キャッシュHIT率を分けて測ると、同じ「遅い」の中に違う原因が見えてきます。