2026年7月12日

2026年7月12日

Apacheのmod_expiresでブラウザキャッシュを設定する方法

はじめに

GooglePageSpeedやGTmetrixで「ブラウザキャッシュを活用してください」と指摘される・WordPressサイトの静的ファイルにCache-Controlヘッダーが設定されていない・mod_expiresの設定方法がわからないといった問題の解決方法を解説します。

症状・原因

  • CSS・JavaScript・画像ファイルにキャッシュヘッダーが設定されていないため毎回サーバーから取得される
  • mod_expiresが有効になっていない(a2enmod expiresが必要)
  • .htaccessにキャッシュ設定を書いたがAllowOverride Noneで無効になっている
  • キャッシュ期間が長すぎてファイル更新後も古いバージョンが表示される

解決手順

ステップ1:mod_expires を有効化する

# ✅ mod_expires が有効か確認
apache2ctl -M | grep expires
# → expires_module (shared) ← 有効
# → 何も表示されない場合は無効

# ✅ mod_expires を有効化
sudo a2enmod expires
sudo a2enmod headers  # Cache-Control ヘッダーに必要

# ✅ Apache を再起動
sudo systemctl reload apache2

# ✅ 現在のキャッシュヘッダーを確認
curl -sI http://example.com/wp-content/themes/theme/style.css | grep -i cache
# → Cache-Control: max-age=2592000 ← 設定済み
# → 何も表示されない ← 未設定

ステップ2:.htaccess にキャッシュ設定を追加する

# ✅ /var/www/html/.htaccess に追加(WordPress 標準設定の外に追加)

# ✅ ブラウザキャッシュ設定(mod_expires)
<IfModule mod_expires.c>
    ExpiresActive On

    # デフォルト(設定されていないファイル)
    ExpiresDefault "access plus 1 month"

    # HTML(頻繁に更新される)
    ExpiresByType text/html "access plus 0 seconds"

    # CSS・JavaScript(バージョン付きURLを推奨)
    ExpiresByType text/css                "access plus 1 year"
    ExpiresByType application/javascript  "access plus 1 year"
    ExpiresByType text/javascript         "access plus 1 year"

    # 画像
    ExpiresByType image/jpeg "access plus 1 year"
    ExpiresByType image/png  "access plus 1 year"
    ExpiresByType image/gif  "access plus 1 year"
    ExpiresByType image/webp "access plus 1 year"
    ExpiresByType image/svg+xml "access plus 1 year"
    ExpiresByType image/x-icon  "access plus 1 year"

    # フォント
    ExpiresByType font/woff2          "access plus 1 year"
    ExpiresByType font/woff           "access plus 1 year"
    ExpiresByType application/font-woff2 "access plus 1 year"

    # その他の静的ファイル
    ExpiresByType application/pdf "access plus 1 month"
    ExpiresByType video/mp4       "access plus 1 month"
</IfModule>

# ✅ Cache-Control ヘッダーを追加(mod_headers)
<IfModule mod_headers.c>
    # immutable フラグでブラウザに変更なしを通知(CSS/JS)
    <FilesMatch "\.(css|js)$">
        Header append Cache-Control "public, immutable"
    </FilesMatch>

    # 画像は public キャッシュを許可
    <FilesMatch "\.(jpg|jpeg|png|gif|webp|svg|ico)$">
        Header append Cache-Control "public"
    </FilesMatch>
</IfModule>

ステップ3:Apache 設定ファイルに直接記述する(推奨)

# ✅ /etc/apache2/sites-available/wordpress.conf に記述(.htaccess より推奨)
<VirtualHost *:80>
    ServerName example.com
    DocumentRoot /var/www/html

    <IfModule mod_expires.c>
        ExpiresActive On
        ExpiresDefault "access plus 1 month"

        ExpiresByType text/html          "access plus 0 seconds"
        ExpiresByType text/css           "access plus 1 year"
        ExpiresByType application/javascript "access plus 1 year"
        ExpiresByType image/jpeg         "access plus 1 year"
        ExpiresByType image/png          "access plus 1 year"
        ExpiresByType image/webp         "access plus 1 year"
        ExpiresByType font/woff2         "access plus 1 year"
    </IfModule>

    <IfModule mod_headers.c>
        # ETags を無効化(Last-Modified と重複)
        Header unset ETag
        FileETag None

        # CSS/JS にバージョン管理が必要な場合のバイパス
        # ?ver=X.X.X パラメータがある場合はキャッシュを許可
    </IfModule>
</VirtualHost>

ステップ4:WordPress のファイルバージョニングを設定する

// ✅ WordPress は CSS/JS に自動的にバージョンクエリを付与する
// wp_enqueue_style('theme-style', get_stylesheet_uri(), [], '1.0.0');
// → style.css?ver=1.0.0

// ✅ バージョンをファイルの更新時刻に基づいて自動更新
add_action('wp_enqueue_scripts', function(): void {
    $style_file = get_stylesheet_directory() . '/style.css';
    $version = file_exists($style_file) ? filemtime($style_file) : '1.0.0';

    wp_enqueue_style(
        'theme-style',
        get_stylesheet_uri(),
        [],
        (string) $version  // → ファイル更新時に自動的にバージョンが変わる
    );
});

// ✅ クエリ文字列を削除(CDN が ?ver= 付きURLをキャッシュしない場合)
add_filter('style_loader_src', 'remove_query_strings', 10, 2);
add_filter('script_loader_src', 'remove_query_strings', 10, 2);

function remove_query_strings(string $src): string {
    if (strpos($src, '?ver=') !== false) {
        $src = preg_replace('/\?ver=[^&]+/', '', $src);
    }
    return $src;
}

ステップ5:キャッシュ設定を確認・テストする

# ✅ curl でキャッシュヘッダーを確認
curl -sI http://example.com/wp-content/themes/theme/style.css
# → Cache-Control: max-age=31536000, public, immutable
# → Expires: Thu, 01 Jan 2026 00:00:00 GMT
# → Last-Modified: Mon, 01 Jan 2024 00:00:00 GMT

# ✅ 2回目のリクエストで304が返るか確認
curl -sI -H "If-Modified-Since: $(date -u '+%a, %d %b %Y %H:%M:%S GMT')" \
    http://example.com/wp-content/themes/theme/style.css
# → HTTP/1.1 304 Not Modified ← キャッシュが有効

# ✅ PageSpeed Insights でスコアを確認
# https://pagespeed.web.dev/

# ✅ Cache-Control ヘッダーの値の意味
# max-age=31536000: 1年間(秒単位)
# public: CDNやプロキシでもキャッシュ可能
# immutable: ファイルは変更されないことをブラウザに通知
# no-store: キャッシュしない
# no-cache: 必ずサーバーに確認してからキャッシュを使用

注意事項

  • CSS・JavaScriptに1年間のキャッシュを設定する場合は、ファイルを更新したときにURLも変更する必要があります(キャッシュバスティング)。WordPressのwp_enqueue_style/scriptは自動的に?ver=バージョンを付与するため、バージョン番号を更新するかファイルの更新時刻をバージョンとして使用してください
  • mod_expiresmod_headersの両方でキャッシュを設定すると競合する場合があります。mod_expiresCache-Control: max-ageを設定し、mod_headersでさらに設定を追加(append)するのが一般的な方法です

まとめ

mod_expiresでのブラウザキャッシュ設定は①a2enmod expires headersでモジュール有効化・apache2ctl -Mで確認・curl -sIでキャッシュヘッダーの現在状態確認、②.htaccessExpiresActive Onと種別ごとの有効期間設定(HTML:0秒/CSS-JS-画像:1年)・Cache-Control: public, immutableをmod_headersで追加、③Apache設定ファイルに直接記述(.htaccessより推奨)・ETagを無効化してLast-Modifiedのみ使用、④WordPressのwp_enqueue_styleでバージョン番号を自動的にファイル更新時刻に設定してキャッシュバスティング実現、⑤curl -sIでキャッシュヘッダー確認・304レスポンスの確認・PageSpeed Insightsでスコアを検証の手順で設定します。

お気軽にご相談ください

お見積りへ お問い合わせへ