2026年8月22日
2026年8月22日
WordPressで画像をLazy Loadして表示速度を改善する方法
はじめに
「画像の多いページがLCP(最大コンテンツ描画)の悪化で遅い」「スクロールしないと見えない画像まで最初に全部読み込んでいる」「Core Web VitalsのスコアでLCPとTBTが低い」——画像のLazy Loadで初期ページロードを大幅に削減できます。
症状・原因
WordPressはバージョン5.5からネイティブのloading="lazy"属性を自動付与していますが、LCPに影響するファーストビュー画像には付与すべきではありません。また背景画像やiframeは別途対応が必要です。
解決手順
ステップ1:ネイティブLazy Loadの動作を確認・調整する
// WordPressはデフォルトでloading="lazy"を付与
// 最初の画像(LCP候補)には付与しないよう制御する
// wp_lazy_loading_enabled フィルターで制御
add_filter( 'wp_lazy_loading_enabled', function(
bool $default,
string $tag_name,
string $context
): bool {
// iframeには適用しない
if ( 'iframe' === $tag_name ) {
return false;
}
return $default;
}, 10, 3 );
// the_content の最初の画像には loading=eager を設定(LCP改善)
add_filter( 'the_content', function( string $content ): string {
// 最初のimgタグだけ eager に変更
$content = preg_replace_callback(
'/<img([^>]+)loading=["\']lazy["\']([^>]*)>/i',
function( array $matches ) use ( &$replaced ): string {
if ( ! isset( $replaced ) ) {
$replaced = true;
return '<img' . $matches[1] . 'loading="eager"' . $matches[2] . '>';
}
return $matches[0];
},
$content
);
return $content;
} );
ステップ2:アイキャッチ画像にfetchpriority=highを設定する
// LCPのアイキャッチ画像に fetchpriority="high" を追加(WordPress 6.3+)
add_filter( 'wp_get_attachment_image_attributes', function(
array $attr,
WP_Post $attachment,
mixed $size
): array {
// 単一投稿ページのアイキャッチのみ対象
if ( is_singular() && has_post_thumbnail() ) {
$thumbnail_id = get_post_thumbnail_id();
if ( $thumbnail_id === $attachment->ID ) {
$attr['loading'] = 'eager';
$attr['fetchpriority'] = 'high';
$attr['decoding'] = 'sync';
}
}
return $attr;
}, 10, 3 );
// wp_head でLCPリソースをpreloadする
add_action( 'wp_head', function(): void {
if ( ! is_singular() || ! has_post_thumbnail() ) return;
$thumbnail_url = get_the_post_thumbnail_url( null, 'large' );
if ( ! $thumbnail_url ) return;
printf(
'<link rel="preload" as="image" href="%s" fetchpriority="high">%s',
esc_url( $thumbnail_url ),
"\n"
);
}, 1 );
ステップ3:Intersection Observer APIで背景画像をLazy Loadする
// functions.php: Intersection Observer スクリプトをエンキュー
add_action( 'wp_enqueue_scripts', function(): void {
wp_enqueue_script(
'lazy-bg',
get_theme_file_uri( 'assets/js/lazy-bg.js' ),
[],
'1.0.0',
[ 'strategy' => 'defer' ]
);
} );
// assets/js/lazy-bg.js
// Intersection Observer で .lazy-bg クラスの要素を監視
document.addEventListener('DOMContentLoaded', () => {
const lazyBgs = document.querySelectorAll('[data-bg]');
if (!lazyBgs.length) return;
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const el = entry.target;
el.style.backgroundImage = `url(${el.dataset.bg})`;
el.classList.add('bg-loaded');
observer.unobserve(el);
}
});
}, {
rootMargin: '200px 0px', // 200px手前から読み込み開始
threshold: 0
});
lazyBgs.forEach(el => observer.observe(el));
});
// テンプレートでの使い方
function render_lazy_bg_section( int $attachment_id, string $content ): void {
$img_url = wp_get_attachment_image_url( $attachment_id, 'full' );
printf(
'<section class="hero lazy-bg" data-bg="%s">%s</section>',
esc_url( $img_url ),
wp_kses_post( $content )
);
}
ステップ4:動画とiframeをLazy Loadする
// YouTube/Vimeo埋め込みを Facade パターンでLazy Load
add_filter( 'the_content', function( string $content ): string {
// YouTube iframeをファサードに置き換え
$content = preg_replace_callback(
'/<iframe[^>]+src=["\']https:\/\/www\.youtube\.com\/embed\/([a-zA-Z0-9_-]+)[^"\']*["\'][^>]*><\/iframe>/i',
function( array $matches ): string {
$video_id = $matches[1];
$thumbnail = "https://img.youtube.com/vi/{$video_id}/maxresdefault.jpg";
return sprintf(
'<div class="yt-facade" data-videoid="%s" style="background-image:url(%s);aspect-ratio:16/9;cursor:pointer;background-size:cover">
<button class="yt-play-btn" aria-label="動画を再生">▶</button>
</div>',
esc_attr( $video_id ),
esc_url( $thumbnail )
);
},
$content
);
return $content;
} );
// YouTube Facadeクリックで本物のiframeを挿入
document.querySelectorAll('.yt-facade').forEach(facade => {
facade.addEventListener('click', () => {
const id = facade.dataset.videoid;
const iframe = document.createElement('iframe');
iframe.src = `https://www.youtube.com/embed/${id}?autoplay=1`;
iframe.allow = 'autoplay; encrypted-media';
iframe.allowFullscreen = true;
iframe.style.cssText = 'width:100%;aspect-ratio:16/9;border:0';
facade.replaceWith(iframe);
});
});
ステップ5:Lazy Load効果をCore Web Vitalsで測定する
# WP-CLI で PageSpeed Insights を確認
# Chrome DevTools > Lighthouse タブで LCP・CLS を計測
# 画像の最適化状況を確認するbashスクリプト
find wp-content/uploads -name "*.jpg" -o -name "*.png" | while read f; do
size=$(stat -c%s "$f")
if [ $size -gt 204800 ]; then # 200KB以上
echo "Large: $f ($(( size / 1024 ))KB)"
fi
done
// 管理画面に最適化されていない画像を表示
add_action( 'admin_notices', function(): void {
if ( ! current_user_can( 'manage_options' ) ) return;
$screen = get_current_screen();
if ( $screen->id !== 'upload' ) return;
$large_images = new WP_Query( [
'post_type' => 'attachment',
'post_mime_type' => 'image',
'posts_per_page' => 5,
'meta_query' => [
[
'key' => '_wp_attachment_metadata',
'compare' => 'EXISTS',
],
],
'no_found_rows' => true,
] );
// 200KB以上の画像をカウントして表示...
} );
注意事項
- ファーストビューのLCP画像に
loading="lazy"を設定すると、LCPスコアが悪化します。アイキャッチや最初の画像にはloading="eager"とfetchpriority="high"を設定してください。 Intersection ObserverはIE11で非対応です。必要な場合はpolyfillを使用するか、単純にloading="lazy"属性のみのフォールバックで十分です。- YouTube埋め込みをFacadeパターンにすると初期ロードが大幅に軽くなりますが、自動再生が期待できないためUX面での設計が必要です。
まとめ
Lazy Load実装は「WordPress標準のloading="lazy"を確認→ファーストビュー画像にloading="eager"+fetchpriority="high"+→背景画像はIntersection Observerでdata-bg属性から読み込み→YouTube/iframeはFacadeパターンで初期DOM軽量化→LighthouseでLCP・TBTを継続計測」の流れで整備します。関連記事:WordPressのWebP画像自動変換を実装する方法、WordPressのCritical CSSを生成する方法。