5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

WordPress で Apache の mod_cache を設定する

Last updated at Posted at 2018-01-17

やりたいこと

  • WordPress を動かしている Apache HTTP Server (Version 2.4) で mod_cache (mod_cache_disk) を設定したい。
  • WordPress は mod_rewrite で Pretty Permalinks を設定している。
  • リクエスト先(一覧 or 記事など)によってキャッシュの可否・有効期限を変更したい。

分かったこと

  • すべてのリクエスト(レスポンス)に対するキャッシュは rewrite 後のURL (/index.php) のキャッシュとして扱われる。
  • よって、特定のURLやディレクトリをキャッシュさせたい場合でも CacheEnable の対象に /index.php が含まれている必要がある。
  • rewrite 後のURL (/index.php) をリクエストされたURL毎にユニークになるようにしなければならない(そうしないとキャッシュされる内容がすべて同一になる)。
  • Location ディレクティブなどを使ってキャッシュの設定やレスポンスヘッダを振り分けることができないので、キャッシュの可否・有効期限の制御は WordPress 側でやる。

Apache のデバッグログ: http://localhost:80/141412 へのリクエストに対して、最終的に Headers and body for URL http://localhost:80/index.php? cached. となっている。

[cache:debug] [pid 16] cache_storage.c(664): [client 172.20.0.8:59718] AH00698: cache: Key for entity /141412?(null) is http://localhost:80/141412?, referer: https://localhost/
[cache:debug] [pid 16] mod_cache.c(210): [client 172.20.0.8:59718] AH00750: Adding CACHE_SAVE filter for /141412, referer: https://localhost/
[cache:debug] [pid 16] mod_cache.c(220): [client 172.20.0.8:59718] AH00751: Adding CACHE_REMOVE_URL filter for /141412, referer: https://localhost/
[cache:debug] [pid 16] cache_storage.c(664): [client 172.20.0.8:59718] AH00698: cache: Key for entity /index.php?(null) is http://localhost:80/index.php?, referer: https://localhost/
[cache_disk:debug] [pid 16] mod_cache_disk.c(572): [client 172.20.0.8:59718] AH00709: Recalled cached URL info header http://localhost:80/index.php?, referer: https://localhost/
[cache_disk:debug] [pid 16] mod_cache_disk.c(885): [client 172.20.0.8:59718] AH00720: Recalled headers for URL http://localhost:80/index.php?, referer: https://localhost/
[cache:debug] [pid 16] cache_util.c(735): [client 172.20.0.8:59718] AH00782: Cache lock obtained for stale cached URL, revalidating entry: /index.php, referer: https://localhost/
[cache:debug] [pid 16] cache_storage.c(359): [client 172.20.0.8:59718] AH00695: Cached response for /index.php isn't fresh. Adding conditional request headers., referer: https://localhost/
[cache:debug] [pid 16] mod_cache.c(210): [client 172.20.0.8:59718] AH00750: Adding CACHE_SAVE filter for /index.php, referer: https://localhost/
[cache:debug] [pid 16] mod_cache.c(220): [client 172.20.0.8:59718] AH00751: Adding CACHE_REMOVE_URL filter for /index.php, referer: https://localhost/
[cache:debug] [pid 16] mod_cache.c(1332): [client 172.20.0.8:59718] AH00769: cache: Caching url: /index.php, referer: https://localhost/
[cache:debug] [pid 16] mod_cache.c(1338): [client 172.20.0.8:59718] AH00770: cache: Removing CACHE_REMOVE_URL filter., referer: https://localhost/
[cache_disk:debug] [pid 16] mod_cache_disk.c(1350): [client 172.20.0.8:59718] AH00737: commit_entity: Headers and body for URL http://localhost:80/index.php? cached., referer: https://localhost/

設定例

mod_cache

# CacheRoot は `a2enmod cache_disk` で mod_cache_disk を有効にすると勝手に設定される模様
# see: /etc/apache2/mods-available/cache_disk.conf

CacheEnable disk /
CacheDisable /wp-admin/
CacheDisable /wp-content/
CacheDisable /wp-includes/

# Set-Cookie がある場合はキャッシュしない
CacheIgnoreHeaders Set-Cookie

# リクエストに no-cache があってもキャッシュを返す
CacheIgnoreCacheControl On

mod_rewrite

/index.php?cache_key=%{REQUEST_URI} のようにリクエストされたURLをクエリとして付加することで、別のURLとして扱ってくれる。

<Directory "/var/www/html">
  RewriteEngine On
  RewriteBase /
  RewriteRule ^index\.php$ - [L]
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteRule . /index.php?cache_key=%{REQUEST_URI} [QSA,L]
</Directory>

修正 (2018-01-22)

/index.php?cache_key=%{REQUEST_URI} への rewrite は、 ?cache_key= によってクエリが上書かれてしまうので、 QSA フラグを付ける必要がある。1

ref: https://httpd.apache.org/docs/2.4/rewrite/flags.html#flag_qsa

WordPress

テーマの functions.php かプラグインで、以下のように設定する。(上記の設定の場合、ExpiresCache-Control ヘッダーを付加しない限りキャッシュされない)

// 'send_headers' フックだと is_single() などが使えない。
add_action('template_redirect', function () {
    if (is_user_logged_in()) {
        return;
    }
    
    if (is_single()) {
        header('Cache-Control: s-maxage=60');
    }
});

クエリ付きのURLは CacheDefaultExpireCacheMinExpire の値は反映されない模様。

If the URL included a query string (e.g. from a HTML form GET method) it will not be cached unless the response specifies an explicit expiration by including an "Expires:" header or the max-age or s-maxage directive of the "Cache-Control:" header, as per RFC2616 sections 13.9 and 13.2.1.
(https://httpd.apache.org/docs/2.4/en/caching.html)

結果

CacheDetailHeader On とすることで、X-Cache-Detail ヘッダーで詳細を確認できる。

Cache-Control ヘッダーを設定したレスポンス

1回目 (cache miss)

2回目 (cache hit)

Apache のデバッグログ: http://localhost:80/141412 へのリクエストに対して、最終的に Headers and body for URL http://localhost:80/index.php?cache_key=/141412 cached. となっている。

[cache:debug] [pid 17] cache_storage.c(664): [client 172.20.0.8:51730] AH00698: cache: Key for entity /141412?(null) is http://localhost:80/141412?, referer: https://localhost/
[cache:debug] [pid 17] mod_cache.c(210): [client 172.20.0.8:51730] AH00750: Adding CACHE_SAVE filter for /141412, referer: https://localhost/
[cache:debug] [pid 17] mod_cache.c(220): [client 172.20.0.8:51730] AH00751: Adding CACHE_REMOVE_URL filter for /141412, referer: https://localhost/
[cache:debug] [pid 17] cache_storage.c(664): [client 172.20.0.8:51730] AH00698: cache: Key for entity /index.php?cache_key=/141412 is http://localhost:80/index.php?cache_key=/141412, referer: https://localhost/
[cache_disk:debug] [pid 17] mod_cache_disk.c(572): [client 172.20.0.8:51730] AH00709: Recalled cached URL info header http://localhost:80/index.php?cache_key=/141412, referer: https://localhost/
[cache_disk:debug] [pid 17] mod_cache_disk.c(885): [client 172.20.0.8:51730] AH00720: Recalled headers for URL http://localhost:80/index.php?cache_key=/141412, referer: https://localhost/
[cache:debug] [pid 17] cache_util.c(735): [client 172.20.0.8:51730] AH00782: Cache lock obtained for stale cached URL, revalidating entry: /index.php?cache_key=/141412, referer: https://localhost/
[cache:debug] [pid 17] cache_storage.c(359): [client 172.20.0.8:51730] AH00695: Cached response for /index.php isn't fresh. Adding conditional request headers., referer: https://localhost/
[cache:debug] [pid 17] mod_cache.c(210): [client 172.20.0.8:51730] AH00750: Adding CACHE_SAVE filter for /index.php, referer: https://localhost/
[cache:debug] [pid 17] mod_cache.c(220): [client 172.20.0.8:51730] AH00751: Adding CACHE_REMOVE_URL filter for /index.php, referer: https://localhost/
[cache:debug] [pid 17] mod_cache.c(1332): [client 172.20.0.8:51730] AH00769: cache: Caching url: /index.php?cache_key=/141412, referer: https://localhost/
[cache:debug] [pid 17] mod_cache.c(1338): [client 172.20.0.8:51730] AH00770: cache: Removing CACHE_REMOVE_URL filter., referer: https://localhost/
[cache_disk:debug] [pid 17] mod_cache_disk.c(1350): [client 172.20.0.8:51730] AH00737: commit_entity: Headers and body for URL http://localhost:80/index.php?cache_key=/141412 cached., referer: https://localhost/

Cache-Control ヘッダーを設定していないレスポンス

キャッシュされない。

User-Agent によって表示を分けている場合

WordPress 側で User-Agent によって表示を出し分けている場合は、Apache にキャッシュさせる内容も分ける必要がある。その場合、/index.php に渡すクエリを User-Agent (表示内容)によって変わるようにすることで対応できる。

(mod_cache は Vary ヘッダーを見ているので、Vary ヘッダーに User-Agent を追加することでキャッシュを分けることもできるが、無数にある User-Agent 毎にキャッシュが生成されてしまうため、キャッシュの効果が薄れる)

# デバイスの判定・出し分けのロジックはWordPress側と合わせる(以下は簡略版)

BrowserMatchNoCase ".*"              layout=desktop
BrowserMatchNoCase "(ipod|iphone)"   layout=mobile
BrowserMatchNoCase "android.*mobile" layout=mobile

<Directory "/var/www/html">
  RewriteEngine On
  RewriteBase /
  RewriteRule ^index\.php$ - [L]
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  # この場合 %{ENV:layout} には 'desktop' or 'mobile' が渡される
  RewriteRule . /index.php?cache_key=%{REQUEST_URI}:%{ENV:layout} [QSA,L]
</Directory>

このとき、WordPress側でクライアントキャッシュも有効にすると(max-age など)、同一ブラウザにおいて User-Agent を変更した場合に、同じクライアントキャッシュが使い回されてしまう。これを防ぐためには Vary ヘッダーに User-Agent を追加する必要があるが、前述の通りキャッシュの意味がなくなる。

(途中で User-Agent を変更するのは開発時くらいだから無視してもいいかもしれないが、モバイルブラウザの「PC版サイトをリクエスト」とかも影響あるかも)

備考

  • Apache のその他の設定や、WordPress の挙動、テーマやプラグインによっては、独自にレスポンスヘッダーを追加してることもあるので気をつける。
  • mod_cache は細かい制御が難しいので、リアルタイム性が求められるコンテンツやユーザーによって変化するコンテンツが含まれている場合はしんどいかも。

参考

  1. この場合、クエリの違い毎にキャッシュが生成されるので、キャッシュ内容に関係ないクエリについては CacheIgnoreURLSessionIdentifiers 等で工夫する。

5
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?