2026年8月3日
2026年8月3日
Apacheのキャッシュ設定(mod_cache)|仕組み・設定例とWordPressでの注意点
mod_cache は、Apache が生成した応答をサーバー側に保存し、同じリクエストに保存済みの内容を返すためのモジュールです。正しく使えば PHP やバックエンドの処理を省けますが、WordPress の HTML に使う場合は、ログイン中のページを他人に配信しないための対策が欠かせません。この記事では、mod_cache の仕組みと関連モジュール、ディスクキャッシュの設定例、WordPress で使うときの注意点、動作確認と削除の方法を説明します。
mod_cache で何ができて、何ができないか
「Apache のキャッシュ設定」と言うとき、実際には次の3つが混同されがちです。最初に区別しておくと、自分に必要なものが選びやすくなります。
- サーバー側キャッシュ(mod_cache):Apache が応答を保存し、次のリクエストにはバックエンドや PHP を通さずに返します。この記事の主題です。
- ブラウザキャッシュ(mod_expires・mod_headers):応答に
Cache-ControlやExpiresヘッダーを付け、訪問者のブラウザに保存してもらいます。画像や CSS の再取得を減らす目的で、静的ファイルにはまずこちらを設定します。 - WordPress のページキャッシュ(プラグイン):WordPress の仕組みを理解したうえで HTML を保存し、ログイン状態やコメント投稿者の Cookie を見て配信を切り替えます。
mod_cache は HTTP のキャッシュの規則(Cache-Control、Expires、Last-Modified など)に従って保存するかを判断する、汎用的な仕組みです。WordPress のログイン Cookie を理解して自動で切り替えてくれるわけではありません。この点が WordPress で使うときの最大の注意点になります。
関連モジュールの役割
Apache 2.4 では、mod_cache は「枠組み」で、実際の保存先は別のモジュールが担当します。
mod_cache:キャッシュの判断と制御を行う本体です。mod_cache_disk:応答をディスクに保存します。一般的に使われるのはこちらです。mod_cache_socache:共有オブジェクトキャッシュ(メモリなど)に保存します。小さな応答向けです。htcacheclean:ディスクキャッシュの容量を管理し、古いファイルを削除するコマンドです。
Apache 2.2 にあった mod_mem_cache は 2.4 で廃止されています。古い記事の設定例をそのまま使わないよう注意してください。
ディスクキャッシュを有効にする
ここでは Debian/Ubuntu 系の VPS を例にします。作業前に /etc/apache2/ をバックアップし、ステージング環境で試してから本番に適用してください。
sudo a2enmod cache cache_disk headers
sudo apachectl configtest
sudo systemctl restart apache2
続いて VirtualHost の設定にキャッシュの指定を加えます。
<IfModule mod_cache_disk.c>
CacheQuickHandler off
CacheRoot /var/cache/apache2/mod_cache_disk
CacheEnable disk /
CacheDirLevels 2
CacheDirLength 1
CacheDefaultExpire 600
CacheMaxExpire 86400
CacheLock on
CacheHeader on
CacheIgnoreHeaders Set-Cookie
CacheDisable /wp-admin
CacheDisable /wp-login.php
CacheDisable /wp-json
</IfModule>
各項目の意味は次のとおりです。
CacheQuickHandler off:既定値は on で、リクエストの処理のごく早い段階でキャッシュを返します。高速ですが、アクセス制限や認証などの処理を通らずに配信されることがあります。アクセス制限を併用するサイトでは off にします。CacheEnable disk /:サイト全体をディスクキャッシュの対象にします。対象を絞りたい場合は/を特定のパスにします。CacheDefaultExpire:応答に有効期限の情報が無いときに使う保存秒数です。CacheMaxExpire:応答が長い有効期限を示していても、これ以上は保存しない上限の秒数です。CacheLock on:キャッシュの期限が切れた瞬間に同じ URL へのリクエストが集中しても、バックエンドへの問い合わせを1つにまとめます。CacheHeader on:応答にX-Cacheヘッダーを付け、キャッシュから返したかどうかを確認できるようにします。CacheIgnoreHeaders Set-Cookie:保存する応答からSet-Cookieヘッダーを取り除きます。ある訪問者向けの Cookie が別の訪問者に配られる事故を防ぐための設定です。CacheDisable:指定したパスをキャッシュの対象外にします。
CacheRoot のディレクトリは、Apache の実行ユーザーが書き込める必要があります。Debian 系では通常パッケージが用意しますが、別の場所を使う場合は所有者を確認してください。RHEL 系では /var/cache/httpd/ 以下が使われるのが一般的です。
WordPress の HTML をキャッシュするときの注意
WordPress は、通常の投稿ページの HTML に Last-Modified や Expires を付けません。mod_cache は、こうした情報が無い応答を既定では保存しないため、上の設定だけでは HTML がほとんどキャッシュされないことがあります。CacheIgnoreNoLastMod On を加えると保存されるようになりますが、そうすると今度は「保存してはいけないページ」を自分で除外する責任が生まれます。
特に注意が必要なのは次のページです。
- ログイン中のユーザーが見るページ(管理バーや個人向けの表示を含む)
- コメントを投稿した直後の、投稿者情報が入ったページ
- パスワード保護された投稿
- EC サイトのカート・購入手続き・マイページ
mod_cache は WordPress のログイン Cookie を見て保存を止める仕組みを持っていません。対策として、ログイン中などの応答に Cache-Control: private を付け、mod_cache に保存させないようにします。テーマの functions.php ではなく、mu-plugin として次のようなファイルを置く方法が、テーマの変更に左右されず管理しやすくなります。
<?php
// wp-content/mu-plugins/no-shared-cache.php
add_action( 'send_headers', function () {
$has_personal_cookie = false;
foreach ( array_keys( $_COOKIE ) as $name ) {
if ( str_starts_with( $name, 'comment_author_' ) || str_starts_with( $name, 'wp-postpass_' ) ) {
$has_personal_cookie = true;
break;
}
}
if ( is_user_logged_in() || $has_personal_cookie ) {
header( 'Cache-Control: private, no-cache, max-age=0' );
}
} );
send_headers は WordPress が応答ヘッダーを送る段階で実行されるアクションです。str_starts_with() は PHP 8.0 以降の関数なので、それより古い PHP では strpos( $name, 'comment_author_' ) === 0 に置き換えます。
ただし、この方法で防げるのは「個人向けのページを保存すること」です。すでに保存されている匿名向けのページは、ログイン中のユーザーにもそのまま返されることがあります(管理バーが出ないなどの違和感が出ます)。ログイン状態に応じた配信の切り替えまで必要なら、WordPress 用のページキャッシュプラグインのほうが扱いやすいのが実情です。mod_cache は、リバースプロキシとして別のサーバーの応答をキャッシュする場合や、更新頻度が低くログイン機能の無いサイトに向いています。
静的ファイルにはブラウザキャッシュを設定する
画像・CSS・JavaScript・フォントは、mod_cache よりも、mod_expires でブラウザに保存してもらうほうが効果的です。Apache はこれらのファイルをもともと高速に返すため、サーバー側で保存しても得られる差は小さく、ブラウザが再取得しないことのほうが表示速度に効きます。
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType image/webp "access plus 1 month"
ExpiresByType image/jpeg "access plus 1 month"
ExpiresByType image/png "access plus 1 month"
ExpiresByType text/css "access plus 1 week"
ExpiresByType application/javascript "access plus 1 week"
ExpiresByType font/woff2 "access plus 1 month"
</IfModule>
WordPress は CSS や JavaScript の URL に ?ver= のバージョン番号を付けることが多く、テーマやプラグインを更新すると URL が変わるため、期限を長めにしても古いファイルが残りにくい仕組みになっています。mod_expires は sudo a2enmod expires で有効にでき、レンタルサーバーでも .htaccess に書ける場合があります。
動作確認とキャッシュの削除
CacheHeader on を設定していれば、curl で応答ヘッダーを見るとキャッシュの状態が分かります。
curl -sI https://example.com/sample-page/ | grep -i "x-cache"
1回目は X-Cache: MISS、2回目以降に X-Cache: HIT となれば保存と配信ができています。期限切れで再確認した場合は REVALIDATE と表示されます。何度アクセスしても MISS のままなら、応答に Cache-Control: private や no-store が付いていないか、有効期限の情報が無いために保存されていないかを、curl -sI の全ヘッダーで確認します。
ログインした状態でブラウザから確認すると、ログイン Cookie の影響で結果が変わります。確認はログアウトした状態か、curl で行うと判断を誤りにくくなります。
ディスクキャッシュは放置すると増え続けるため、htcacheclean で容量を管理します。
# 60分ごとに実行し、合計1GB以内に保つ(デーモンとして常駐)
sudo htcacheclean -d60 -n -t -p /var/cache/apache2/mod_cache_disk -l 1024M
ディストリビューションによっては htcacheclean を常駐させるサービスが用意されているので、パッケージの説明を確認してください(2026年10月現在)。記事を更新してもすぐに反映されない場合、特定の URL だけを消すには htcacheclean -p キャッシュのディレクトリ URL の形式で URL を指定できます。全体を消す場合は、Apache を止めてからキャッシュディレクトリの中身を削除する方法もありますが、CacheRoot 以外の場所を消さないよう、パスを何度も確認してから実行してください。
まとめ
- mod_cache はサーバー側キャッシュの枠組みで、保存先は mod_cache_disk などが担当する。mod_mem_cache は 2.4 で廃止された
- アクセス制限を併用するなら
CacheQuickHandler off、Cookie の漏えい防止にCacheIgnoreHeaders Set-Cookieを設定する - WordPress の HTML はログイン Cookie を考慮した除外が必要。ログイン状態の切り替えまで必要ならページキャッシュプラグインが扱いやすい
- 画像や CSS は mod_expires のブラウザキャッシュで対応する
X-Cacheヘッダーで HIT/MISS を確認し、htcacheclean で容量を管理する