Article

WordPressサイトの高速化チューニング術:プラグイン選定と設定

高田晃太郎
WordPressサイトの高速化チューニング術:プラグイン選定と設定

統計によれば、W3TechsはWordPressが全Webサイトの約43%を占めると報告しており¹、モバイルでCore Web Vitals(CWV)を満たすサイトの割合はおおむね半数未満にとどまります²。Googleが重視するしきい値は、LCP(Largest Contentful Paint:メインコンテンツの描画開始)が2.5秒以下、INP(Interaction to Next Paint:操作に対する次の描画までの遅延)が200ms以下、CLS(Cumulative Layout Shift:レイアウトのズレ)が0.1以下です³。トラフィック規模の大きいWordPressサイトでは、プラグインの選定と設定の些細な違いがTTFB(Time to First Byte:最初のバイトが届くまでの時間)やLCPに直結し、広告収益やCVRの逓減につながることが現場ではしばしば観測されます⁵。本稿では、WordPress高速化とCore Web Vitals改善を、プラグイン選定と設定にフォーカスして再現性のある手順に落とし込みます。堅実なキャッシュ戦略、メディア最適化、DBとクエリの整流を、コードと設定例で示しながら、運用に耐える形に仕上げます。

計測から始める戦略設計:指標・観測・ボトルネック

最適化の出発点は観測です。PageSpeed InsightsやLighthouseでLCP・INP・CLSのラボ値を把握し、WebPageTestやOrigin/Field Dataでネットワーク条件とTTFBを突き合わせると、レンダリングの遅延がサーバ側のキュー(PHP実行・DBクエリ)由来か、フロント側のリソース肥大(JS/CSS・画像)由来かが見えてきます。運用では、CDN(Content Delivery Network)越しのキャッシュヒット率、アプリ層のオブジェクトキャッシュ命中率、Nginxのfastcgi_cacheステータス、DBのスロークエリログが、日々の体温計になります。測定結果を、ページキャッシュ、オブジェクトキャッシュ、インバリデーション、メディア最適化、クエリ整流という五つのレバーにマッピングし、影響度の高い順から手を入れるのが効率的です。

テスト環境のLCPが3.8秒、TTFBが900ms、INPが260msというケースでは、サーバ側のTTFBが支配的であり、まずはページキャッシュの徹底とオブジェクトキャッシュの導入が費用対効果に優れます。反対にTTFBが200ms台でLCPが3秒超なら、画像・フォント・JSの最適化が先行します。能動的に差分を検証するために、CI(継続的インテグレーション)に計測を組み込み、PRごとにLighthouseスコアやLCPを記録する仕組みを用意すると、設定変更の良し悪しがブレません。

# 計測補助: アクティブプラグインの棚卸し(計測前の前提確認)
wp plugin list --status=active --format=json | jq '.[] | {name:.name, version:.version}'

計測自動化のひと押し:PSI APIでLCP/INPを継続観測

運用では手計測に限界があります。PageSpeed Insights APIを使って、代表URLのLCPとINPをデイリーで記録しておくと、設定変更の影響を定量化できます。次のNodeスクリプトは、APIレスポンスからLCPとINPのp75(75パーセンタイル)を抽出し、CIや監視に取り込むことを想定しています。

// psi-metrics.mjs
import fetch from 'node-fetch';
const url = process.argv[2];
const key = process.env.PSI_API_KEY;
const endpoint = `https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url=${encodeURIComponent(url)}&category=PERFORMANCE&strategy=MOBILE&key=${key}`;
const res = await fetch(endpoint);
const data = await res.json();
const metrics = data.loadingExperience?.metrics || {};
const lcp = metrics.LARGEST_CONTENTFUL_PAINT_MS?.percentile;
const inp = metrics.INTERACTION_TO_NEXT_PAINT?.percentile;
console.log(JSON.stringify({ url, lcp_ms: lcp, inp_ms: inp, timestamp: new Date().toISOString() }));

キャッシュ戦略の中核:ページ・オブジェクト・配信

WordPressの性能を押し上げる支点はキャッシュです。ページキャッシュは未ログイン閲覧に大きな効果をもたらし、オブジェクトキャッシュは複雑なテーマやWooCommerceのようなメタ参照が多い環境で効きを発揮します。プラグイン選定では、サーバとCDNの組み合わせを軸にします。LiteSpeed Web ServerならLiteSpeed Cacheがサーバ機能と密に連携しやすく、HTTP/3やESI(Edge Side Includes)、QUIC.cloudとの統合まで一気通貫で扱えます。Apache+NginxのハイブリッドやNginx単体なら、WP Rocketのような包括的最適化か、W3 Total Cacheのような分解可能な構成が運用方針に合います。静的な要件が強ければ、Cache Enablerのようなシンプルさが事故率の低さに繋がります。重要なのは、ページキャッシュの粒度とパージ戦略、ログイン・動的ページのバイパス条件、オブジェクトキャッシュの永続層(Redis等)です。

NginxでPHPアプリのTTFBを削るには、fastcgi_cacheを正しく張るのが手堅い方法です。未ログイン、クエリ文字列、Cookieを適切に分岐し、投稿更新時のパージはWebhookやCLIから連動させます。

# /etc/nginx/conf.d/wordpress_cache.conf
fastcgi_cache_path /var/cache/nginx levels=1:2 keys_zone=WP:100m inactive=60m max_size=10g;
map $http_cookie $no_cache {
  default 0;
  ~*(wordpress_logged_in|comment_author|woocommerce_cart_hash) 1;
}
server {
  server_name example.com;
  set $cache_key "$scheme$request_method$host$request_uri";
  location / {
    try_files $uri $uri/ /index.php?$args;
  }
  location ~ \.php$ {
    include fastcgi_params;
    fastcgi_pass unix:/run/php/php8.2-fpm.sock;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_cache_bypass $no_cache;
    fastcgi_no_cache $no_cache;
    fastcgi_cache WP;
    fastcgi_cache_key $cache_key;
    add_header X-Cache $upstream_cache_status always;
    add_header Cache-Control "public, max-age=300";
  }
}

オブジェクトキャッシュはRedisを使うのが定番です。Redis Object Cacheプラグインと合わせて、wp-config.phpには接続やTTLなどの定数を定義し、永続化しないグループの指定はmu-pluginやテーマのfunctions.phpで明示します。

// wp-config.php(抜粋:Redis接続まわり)
define('WP_REDIS_HOST', '127.0.0.1');
define('WP_REDIS_PORT', 6379);
define('WP_REDIS_DATABASE', 0);
define('WP_REDIS_MAXTTL', 3600);
define('WP_CACHE_KEY_SALT', 'example_com:');
// mu-plugins/redis-non-persistent.php(永続から除外したいグループ)
<?php
add_action('init', function() {
  if (function_exists('wp_cache_add_non_persistent_groups')) {
    wp_cache_add_non_persistent_groups(['comment', 'counts']);
  }
});

Apache環境では、静的コンテンツの鮮度制御と圧縮が基本です。Brotliが使えるなら優先し、HTTPキャッシュのディレクティブを明快に設定します。

# .htaccess(抜粋)
<IfModule mod_brotli.c>
  AddOutputFilterByType BROTLI_COMPRESS text/html text/css text/javascript application/javascript application/json image/svg+xml
</IfModule>
<IfModule mod_deflate.c>
  AddOutputFilterByType DEFLATE text/plain text/html text/xml text/css application/javascript application/json image/svg+xml
</IfModule>
<IfModule mod_expires.c>
  ExpiresActive On
  ExpiresByType text/css "access plus 7 days"
  ExpiresByType application/javascript "access plus 7 days"
  ExpiresByType image/webp "access plus 30 days"
  ExpiresDefault "access plus 1 hour"
</IfModule>
<IfModule mod_headers.c>
  Header set Cache-Control "public, max-age=3600"
</IfModule>

プラグインの設定では、ログインユーザーやカート保有中の訪問者を必ずキャッシュ対象から外し、モバイルとデスクトップの分割キャッシュが必要かは実測に基づいて判断します。クリティカルCSSの自動生成はLCPの改善に寄与しやすい一方、テーマ更新時に差分が崩れることがあります。運用では、自動生成と手動上書きの二段構えにして、テンプレートごとのヒーロー要素に優先度を寄せるのが安全です。

JS/CSSの負荷低減:読み込み順序と無効化

レンダリングのブロック要因は、往々にしてJSとCSSの読み込み順です。すべてを盲目的に遅延させるより、クリティカルパス(初回描画に必要な最低限のリソース)を守りつつ周辺を遅延・先読みさせる方がLCPは安定します。プラグイン側に任せられない場合は、フィルターで細かく制御します。

// functions.php:jQueryを除きdefer、非同期読み込みを制御
add_filter('script_loader_tag', function($tag, $handle, $src) {
  $exclude = ['jquery-core', 'jquery-migrate'];
  if (in_array($handle, $exclude, true)) return $tag;
  if (strpos($tag, 'defer') === false) {
    $tag = str_replace('<script ', '<script defer ', $tag);
  }
  return $tag;
}, 10, 3);

Heartbeatや絵文字スクリプトの無効化は、INPのばらつきを抑え、管理画面の体感も向上します。

// Heartbeat間隔を延長(編集時以外は無効化)
add_filter('heartbeat_settings', function($settings) {
  $settings['interval'] = 60;
  return $settings;
});
add_action('init', function() {
  if (!is_admin()) wp_deregister_script('heartbeat');
});

// 絵文字を無効化
remove_action('wp_head', 'print_emoji_detection_script', 7);
remove_action('wp_print_styles', 'print_emoji_styles');

メディア・フォントと配信:LCPを支える軽量化

LCP改善の現場で最も効くのはメディア最適化です。画像はAVIF/WebPの自動生成と、上位折り返しの遅延読み込み解除、正確なwidth/height付与で安定します。プラグインの設定では、サーバ側で拡張子差し替えが必要な場合があります。Nginxの設定を使うと、拡張子の分岐を簡潔に制御できます。

# Nginx: AVIF/WebP優先配信(AVIF→WebP→オリジナルの順にフォールバック)
map $http_accept $img_fmt {
  default "orig";
  ~*avif "avif";
  ~*webp "webp";
}
location ~* \.(png|jpe?g)$ {
  set $base $uri;
  if ($img_fmt = avif) { try_files $base.avif $base.webp $base @fallback; }
  if ($img_fmt = webp) { try_files $base.webp $base @fallback; }
  try_files $base @fallback;
}
location @fallback { try_files $uri $uri/ /index.php?$args; }

フォントはサブセット化(必要な文字だけを抜き出す)とdisplay=swap、preloadの三点を押さえるだけで、CLSや初回描画の遅れを抑制できます。CDNを併用する場合、HTMLのキャッシュを積極的に有効化すると、オリジン負荷とTTFBが同時に下がります。Cloudflare Workersでstale-while-revalidateに近い挙動を作ると、再生成の瞬間も安定します。

// Cloudflare Worker:HTMLを短期キャッシュ、staleを許容
export default {
  async fetch(request, env, ctx) {
    const cache = caches.default;
    const url = new URL(request.url);
    if (request.method !== 'GET' || url.pathname.startsWith('/wp-admin')) {
      return fetch(request);
    }
    const cacheKey = new Request(url.toString(), request);
    let response = await cache.match(cacheKey);
    if (!response) {
      const origin = await fetch(request, { cf: { cacheTtl: 300, cacheEverything: true } });
      response = new Response(origin.body, origin);
      response.headers.set('Cache-Control', 'public, max-age=300, stale-while-revalidate=60');
      ctx.waitUntil(cache.put(cacheKey, response.clone()));
    }
    return response;
  }
}

プラグインの設定面では、画像最適化系(EWWW、Imagify、ShortPixelなど)でAVIF/WebP生成と、遅延読込の閾値調整、背景画像とヒーロー画像の除外指定を細かく行うと、視覚的LCPのズレが解消します。動画の埋め込みは、サムネイルを軽量化したプレースホルダーに置き換え、ユーザー操作で初めてプレイヤーを読み込む形が、INPの悪化を防ぎます。

DB・クエリとインバリデーション:整流で持続的に速く

キャッシュに頼るだけでは、更新頻度の高いサイトや検索・フィルタ機能の多いECでは頭打ちになります。クエリの整流とインデックスの最適化、インバリデーションの粒度調整が、再現よく効きます。まず、Query Monitorで遅いクエリと呼び出し元フックを特定し、トラフィックの多いテンプレートから順に直していきます。

メタクエリが多いサイトでは、wp_postmetaに複合インデックスを追加するだけで、顕著にレイテンシが下がることがあります。変更前にバックアップを取り、運用監視下で適用します。

-- メタクエリ向けの複合インデックス(要検証)
CREATE INDEX idx_postmeta_meta_key_value ON wp_postmeta (meta_key(191), meta_value(191));
CREATE INDEX idx_postmeta_post_meta ON wp_postmeta (post_id, meta_key(191));

WP_Queryは小さな配慮で実行コストが変わります。検索結果や一覧でページネーションが不要なら、総件数のカウントを切るだけでDB負荷が軽くなります。必要なフィールドだけ取る方針も、オブジェクトキャッシュのヒットを安定させます。

// WP_Queryの最適化例
$query = new WP_Query([
  'post_type'      => 'post',
  'posts_per_page' => 10,
  'no_found_rows'  => true,        // COUNT(*) をやめる
  'fields'         => 'ids',       // IDだけ取得
  'orderby'        => 'date',
  'order'          => 'DESC',
  'meta_query'     => [
    [ 'key' => 'featured', 'value' => '1' ]
  ]
]);

インバリデーションは、更新のたびに全消しするのではなく、更新対象に関連するアーカイブとトップ数ページを狙い撃ちにします。CLIでパージ対象をスクリプト化しておくと、ECやニュースサイトでも安定します。加えて、疑似リアルタイム性が不要なウィジェットやカウント系は、Transients APIで短時間キャッシュさせると、オブジェクトキャッシュの恩恵も乗ります。

運用では、WP-Cronをシステムのcronに置き換えるだけでも、ピーク帯の突発実行を避け、TTFBのばらつきが抑えられます⁴。

# wp-config.php でWP-Cronを無効化
# (適用時はコメントを外す)
# define('DISABLE_WP_CRON', true)

# crontab登録(例:1分ごと)
* * * * * /usr/bin/php /var/www/html/wp-cron.php >/dev/null 2>&1

設定変更の効果測定と事例の目安

ページキャッシュ導入前後で、未ログインのTTFBが約900msから120〜250msに、LCPが3.8秒から2.2〜2.6秒に落ち着く、といった報告は珍しくありません。オブジェクトキャッシュを追加すると、ログイン閲覧時のTTFBが700msから300〜400msに改善する傾向が見られます。メディア最適化とフォントチューニングを合わせると、INPのp75が260msから170〜190msへ収まるケースも現実的です。もちろんテーマやホスティング、CDNの差分はありますが、測定→設定→検証のループを一巡させるだけで、広告収益やCVRの増分で施策コストを回収できる可能性があるのが、高速化のビジネス的な強みです。

まとめ:運用に耐える高速化は、観測と最小の変更の積み上げ

WordPressの高速化は、魔法のプラグインを当てる話ではありません。観測に基づく最小の変更を、ページキャッシュ、オブジェクトキャッシュ、メディア最適化、クエリ整流の順に積み上げ、設定の粒度を現場に合わせて磨く営みです。今日できる一歩として、代表URLのLCPとINPを計測し、未ログインでのページキャッシュを正しく張るだけでも、体感は大きく変わります。次にRedisの永続オブジェクトキャッシュを導入し、画像のAVIF/WebP化とヒーロー周辺の読み込み順序を整えると、指標は安定します。あなたのチームでは、どのページが最も遅く、どの設定が最も効くでしょうか。計測をCIに組み込み、変更のたびに数字で語れる体制を整えたとき、速度は成果に変わります。小さな改善を確実に積み重ねることが、最大の短縮への最短距離です。

参考文献

  1. W3Techs. Usage statistics and market share of WordPress. https://w3techs.com/technologies/comparison/cm-wordpress#:~:text=This%20diagram%20shows%20the%20percentages,W3Techs.com%2C%2030%20July%202025
  2. HTTP Archive Web Almanac 2024 – Performance. https://almanac.httparchive.org/en/2024/performance#:~:text=experience%20has%20worsened%2C%20just%20that,metric%20we%20use%20at%2054
  3. Google Search Central Help. Core Web Vitals thresholds. https://support.google.com/webmasters/answer/9205520?hl=en-IL&rd=1&visit_id=638791982383647053-4213704995#:~:text=,0.25%20%7C%20%3E0.25
  4. Kinsta. How to Disable WP-Cron (wp-cron.php) in WordPress. https://kinsta.com/knowledgebase/disable-wp-cron/#:~:text=A%20better%20approach%20is%20to,recommended%20in%20the%20official%C2%A0Plugin%20handbook
  5. Kinsta. The Complete Guide to WordPress Performance Optimization. https://kinsta.com/blog/wordpress-performance/#:~:text=Today%E2%80%99s%20users%20are%20more%20impatient,and%20a%20negative%20brand%20perception