KUSANAGI のキャッシュをIPアドレスで除外する

KUSANAGI のキャッシュをIPアドレスで除外する 技術ネタ

KUSANAGI の fcache と bcache を利用しているのですが、特定のIPアドレスからアクセスした場合にキャッシュさせないようにしたかったため、その時の対応した方法についてです。



理由

特定のIPアドレスでアクセスされた際にキャッシュさせないようにしたかった理由はいくつかあるのですが、一番は、記事を書いたり、WordPressをカスタマイズ(PHPコード修正など)したりする際に、キャッシュされてしまい、いちいちキャッシュをクリアーするのが面倒だったため、自分(特定のIPアドレス)はキャッシュさせないようにしようと考えました。

当サイトは KUSANAGI(Ngixn+PHP7)の構成になっています。fcache(Nginx側キャッシュ)と bcache(WordPress側キャッシュ)両方共使用しており、どちらもIPアドレスでキャッシュしないようにする機能はなかったため、fcache は Nginxの設定変更で、bcache はプラグインのカスタマイズで対応することにしました。

具体的な方法

fcache と bcache それぞれに対して、以下の対応を行いました。

fcache

fcacheの具体的な対応ですが、まず、Nginx の geoモジュールを使って、内部アクセスか外部アクセスなのかを判定する変数($access_from)を設定します。
nginx.conf の httpモジュール に以下を追加します。

geo $access_from {
    default external;
    127.0.0.1 internal;
    192.168.0.0/24 internal;
 }

これで、内部アクセスなら $access_from = “internal” 、外部アクセスなら $access_from = “external” となります。

「192.168.0.0/24」はサンプルとして記述しています。自分のアドレスに書き換えてください。
次に、$access_from 変数を使って、内部アクセス(internal)ならキャッシュさせないようにします。
Nginx のプロビジョニング用confファイルに以下を追加します。

set $do_not_cache 0; ## page cache
set $device "pc";

# 内部アクセスはキャッシュしない
if ($access_from = "internal") {
    set $do_not_cache 1;
}

if ($request_method = POST) {
    set $do_not_cache 1;
}

if ($query_string != "") {
    set $do_not_cache 1;
}

「$do_not_cache」に「1」を設定するとキャッシュされないようになります。
locationディレクティブ「 location ~ [^/]\.php(/|$) { 」内に「$do_not_cache」を設定している箇所がありますので、その近辺に 5~7行目を追加してください。
これで、指定したIPアドレスからアクセスされた場合はキャッシュされないようになります。

なお、確認方法についでですが、レスポンスヘッダが「X-F-Cache: BYPASS」となっていればキャッシュされていません。

bcache

bcache の対応は長くなるので、結果から先に書きます。

最終的な対応方法

色々調べましたが、プラグイン自体のコードを修正する方法しかなく、「DocumentRoot/wp-content/mu-plugins/kusanagi-core/modules/page-cache.php」に以下コードを追加しました。

if ( $life_time['exclude'] ) {
    $rules = explode( "\n", $life_time['exclude'] );
    $regex = array();
    foreach ( $rules as $rule ) {
        $regex[] = str_replace( '/', '\/', trim( $rule ) );
    }
    $regex = '/' . implode( '|', $regex ) . '/';
    if ( preg_match( $regex, $_SERVER['REQUEST_URI'] ) ) {
        header( 'X-B-Cache: excluded' );
        return $buffer;
    }
}

if ( defined( 'CACHE_EXCLUDE_IP' ) ) {
    $exclude_ips = explode( '|', CACHE_EXCLUDE_IP );
    foreach ( $exclude_ips as $exclude_ip ) {
        if ( $_SERVER['REMOTE_ADDR'] == $exclude_ip || preg_match( '/' . preg_quote( $exclude_ip ) . '/', $_SERVER['REMOTE_ADDR'] ) ) {
            header( 'X-B-Cache: excluded_ip' );
            return $buffer;
        }
    }
}

「 if ( $life_time[‘exclude’] ) { 」のところが既存のコードで、設定画面から追加したURLを除外する機能です。その下あたりに、14~22行目のコードを追加してください。
次に、config.php に 定数「CACHE_EXCLUDE_IP」に除外したいIPアドレスを設定してください。
例:define(‘CACHE_EXCLUDE_IP’, ‘192.168.0.’)

コード追加と定数「CACHE_EXCLUDE_IP」の両方設定後に、ページにアクセスすると、レスポンスヘッダが「X-B-Cache: excluded_ip」となり、キャッシュされなくなります。
念のため、「kusanagi bcache clear」実行後にアクセスしても、wp_site_cache テーブルにレコードが生成されていないため、キャッシュされないし、キャッシュ自体も生成されない状態となっていました。

page-cache.php に追加したコードですが、これはオリジナルのロジックではなく、advanced-cache.php に書かれているコードをコピペしただけです。詳しくはこの続きを参照してください。

最終的な対応に至るまでの経緯

まず、WordPressのプラグインとして動作しているKUSANAGIのキャッシュ処理コードを読んでみると、「DocumentRoot/wp-content/advanced-cache.php」ファイルに以下のようなコードがありました。

if ( defined( 'CACHE_EXCLUDE_IP' ) ) {
    $exclude_ips = explode( '|', CACHE_EXCLUDE_IP );
    foreach ( $exclude_ips as $exclude_ip ) {
        if ( $_SERVER['REMOTE_ADDR'] == $exclude_ip || preg_match( '/' . preg_quote( $exclude_ip ) . '/', $_SERVER['REMOTE_ADDR'] ) ) {
            return;
        }
    }
}

どうやら、定数「CACHE_EXCLUDE_IP」にIPアドレスを設定すると、キャッシュを返さないようにする機能が既に用意されているようでした。
早速、config.php に define(‘CACHE_EXCLUDE_IP’, ‘192.168.0.’) といったような感じで設定してアクセスしてみたところ、レスポンスヘッダは「X-B-Cache: create」となり、キャッシュされたものは返さないようになりました。

しかし、wp_site_cache テーブルを見ると、キャッシュデータが作成されているため、キャッシュは返さないが、キャッシュ作成処理は実行されている状態でした。
この状態だと、意図しないページデータがキャッシュされてしまい、一般利用者にそのページが表示されてしまう可能性があるため、キャッシュ自体も生成されないようにする方法を模索しました。

キャッシュ自体も生成されない方法を調べたのですが、結論として、上に書いた page-cache.php 自体を修正する方法しかなく、その対応で妥協しました。

まとめ

もし、IPアドレスでキャッシュを除外したい方がいれば参考にしてください。
ただし、bcache についてはプラグイン自体を修正するため、あまりオススメできませんが・・・