2026年8月21日
2026年8月21日
WordPressのCore Web Vitalsを改善する方法【LCP・CLS・INP】
はじめに
Google検索ランキングの要因となるCore Web Vitals(CWV)はLCP・CLS・INPの3指標です。WordPressで低スコアが出やすい原因と改善方法を具体的なコードで解説します。
症状・原因
Core Web Vitalsが低下する主な原因:
- LCP(最大コンテンツ描画):アイキャッチ画像のlazy load・フォントブロッキング
- CLS(累積レイアウトシフト):画像サイズ未指定・遅延読み込み広告
- INP(操作から次の描画まで):重いJavaScript・非最適化イベントハンドラー
解決手順
ステップ1:LCP(最大コンテンツ描画)を改善する
// functions.php
// ファーストビューのアイキャッチ画像にlazy loadを無効化(LCP改善)
add_filter('wp_get_attachment_image_attributes', 'optimize_hero_image_attrs', 10, 3);
function optimize_hero_image_attrs(array $attrs, WP_Post $attachment, $size): array {
// アーカイブページや投稿ページのアイキャッチ(最初の1枚)
static $hero_loaded = false;
if (!$hero_loaded && is_singular() && in_the_loop()) {
// LCPとなるヒーロー画像はeager + fetchpriorityをhighに
$attrs['loading'] = 'eager';
$attrs['fetchpriority'] = 'high';
$attrs['decoding'] = 'sync';
$hero_loaded = true;
}
return $attrs;
}
// LCP画像を<head>でpreloadする
add_action('wp_head', 'preload_hero_image', 1);
function preload_hero_image(): void {
if (!is_singular()) return;
$post_id = get_the_ID();
if (!has_post_thumbnail($post_id)) return;
$thumb_id = get_post_thumbnail_id($post_id);
$image_src = wp_get_attachment_image_srcset($thumb_id, 'large');
$thumb_url = get_the_post_thumbnail_url($post_id, 'large');
if ($thumb_url) {
$srcset = $image_src ? ' imagesrcset="' . esc_attr($image_src) . '"' : '';
echo '<link rel="preload" as="image" href="' . esc_url($thumb_url) . '"'
. $srcset . ' fetchpriority="high">' . PHP_EOL;
}
}
// Google Fontsをpreloadして読み込みを最適化
add_action('wp_head', 'preconnect_google_fonts', 1);
function preconnect_google_fonts(): void {
// フォントの取得元にpreconnectする
echo '<link rel="preconnect" href="https://fonts.googleapis.com">' . PHP_EOL;
echo '<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>' . PHP_EOL;
}
ステップ2:CLS(累積レイアウトシフト)を防ぐ
// 画像タグに必ずwidth/heightを追加してCLSを防ぐ
add_filter('the_content', 'add_missing_image_dimensions');
function add_missing_image_dimensions(string $content): string {
// width/heightがない<img>タグを検索
return preg_replace_callback(
'/<img([^>]*?)(?<!width=")(?<!height=")>/i',
function($matches) {
$img_tag = $matches[0];
// すでにwidth/heightがある場合はスキップ
if (preg_match('/width\s*=/i', $img_tag) || preg_match('/height\s*=/i', $img_tag)) {
return $img_tag;
}
// srcからファイル情報を取得
if (preg_match('/src=["\']([^"\']+)["\']/', $img_tag, $src_match)) {
$src = $src_match[1];
$size = @getimagesize($src);
if ($size) {
$img_tag = str_replace('>', " width=\"{$size[0]}\" height=\"{$size[1]}\">", $img_tag);
}
}
return $img_tag;
},
$content
);
}
// アイキャッチ画像に必ずaspect-ratioを設定するCSS
add_action('wp_head', 'output_aspect_ratio_css');
function output_aspect_ratio_css(): void {
echo '<style>
/* CLS防止: 画像の縦横比を事前に確保 */
.wp-post-image,
.attachment-thumbnail,
.size-large {
aspect-ratio: attr(width) / attr(height);
}
/* アイキャッチ画像のCLS防止 */
.post-thumbnail img {
width: 100%;
height: auto;
display: block;
}
/* 広告スペースの高さを予約 */
.ad-slot {
min-height: 250px;
display: flex;
align-items: center;
justify-content: center;
}
</style>' . PHP_EOL;
}
ステップ3:INP(操作から次の描画まで)を改善する
// JavaScriptのINP最適化(wp-content/themes/mytheme/js/inp-optimize.js)
// 重いイベントハンドラーをschedulerで分割
document.addEventListener('click', function handleClick(e) {
const btn = e.target.closest('[data-heavy-task]');
if (!btn) return;
// クリックのビジュアルフィードバックをまず処理(INP改善)
btn.classList.add('is-loading');
btn.setAttribute('disabled', 'true');
// 重い処理をyieldで分割(Scheduler API)
if ('scheduler' in window && 'yield' in scheduler) {
(async () => {
await scheduler.yield(); // ブラウザに描画を譲る
await performHeavyTask(btn);
btn.classList.remove('is-loading');
btn.removeAttribute('disabled');
})();
} else {
// フォールバック: setTimeout でマクロタスクに分割
setTimeout(() => {
performHeavyTask(btn);
btn.classList.remove('is-loading');
btn.removeAttribute('disabled');
}, 0);
}
});
// 入力フォームのイベントをデバウンス(INP改善)
function debounce(fn, delay) {
let timer;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), delay);
};
}
const searchInput = document.getElementById('search-input');
if (searchInput) {
searchInput.addEventListener('input', debounce(function(e) {
// 検索処理(300ms後に実行)
fetchSearchResults(e.target.value);
}, 300));
}
// スクリプトをdeferで非同期読み込み(INP改善)
add_filter('script_loader_tag', 'add_defer_to_scripts', 10, 3);
function add_defer_to_scripts(string $tag, string $handle, string $src): string {
$defer_scripts = ['my-heavy-script', 'comments-js', 'social-share'];
if (in_array($handle, $defer_scripts)) {
return str_replace(' src=', ' defer src=', $tag);
}
return $tag;
}
ステップ4:PageSpeed Insightsで測定・確認する
# PageSpeed Insights API でスコアを取得
curl -s "https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url=https://example.com&strategy=mobile" | \
jq '.lighthouseResult.categories.performance.score'
# Core Web Vitalsの各指標を確認
curl -s "https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url=https://example.com&strategy=mobile" | \
jq '.lighthouseResult.audits | {
LCP: ."largest-contentful-paint".displayValue,
CLS: ."cumulative-layout-shift".displayValue,
TBT: ."total-blocking-time".displayValue
}'
# WP-CLIでリソースヒントを確認
wp eval "echo wp_resource_hints_rels(['preload', 'dns-prefetch', 'preconnect']);"
ステップ5:定期的な自動計測を設定する
# Lighthouse CIをGitHub Actionsで自動実行
# .github/workflows/lighthouse.yml
# - npm install -g @lhci/cli
# - lhci collect --url=https://example.com
# - lhci assert --preset=lighthouse:recommended
# - lhci upload --target=temporary-public-storage
注意事項
fetchpriority="high"はファーストビューの最も重要な画像1枚のみに設定してください- CLSはブラウザのDevToolsの「レイアウトシフトリージョン」機能で視覚的に確認できます
- INPは2024年3月にFID(First Input Delay)の代替として採用された新指標です
まとめ
Core Web Vitals改善は、①LCP画像のpreload + fetchpriority=high設定、②画像のwidth/height属性明示によるCLS防止、③JavaScript分割とdeferによるINP改善、④PageSpeed Insights APIで自動測定、⑤Lighthouse CIでCI/CDに組み込む流れで実施します。