2026年6月6日

2026年6月6日

Envoyでキャッシュを設定する手順|HTTPキャッシュフィルターの構成とWordPressで外すべきページ

Envoy でレスポンスをキャッシュするには、HTTP コネクションマネージャーに HTTP キャッシュフィルター(envoy.filters.http.cache)を追加し、保存先(ストレージ実装)を指定します。この記事では、最小構成の YAML、オリジン(WordPress)側で付けるべき Cache-Control、ログイン中のページをキャッシュから外す方法、ヒットしているかの確認方法までを順に説明します。あわせて、このフィルターが本番利用にどこまで向くかという判断材料も示します。

最初に知っておきたい前提

Envoy のキャッシュフィルターは、Nginx の proxy_cache や Varnish のように長年使われてきた機能とは位置づけが異なります。公式ドキュメントでは開発途上の機能として扱われてきた経緯があり、設定項目や同梱のストレージ実装はバージョンによって変わっています(2026年10月現在)。利用中のバージョンのドキュメントで状態を確認することをおすすめします。

特に、標準で用意されているメモリ上の簡易キャッシュ(SimpleHttpCache)は、容量制限や削除の仕組みが最小限で、大規模な本番運用向けではないと説明されています。ファイルシステムに保存する実装も追加されていますが、こちらも成熟度はバージョン次第です。

そのため、次のように使い分けるのが現実的です。

  • Envoy を既に入口に使っていて、静的ファイルや短時間のキャッシュを足したい:キャッシュフィルターを試す価値があります。
  • キャッシュの削除(パージ)や細かい制御が必要:Envoy の後ろに Varnish や Nginx を置く、または CDN に任せる構成のほうが運用しやすいことが多いです。

作業は本番に入れる前に、検証環境(ステージング)で行ってください。設定ファイルは変更前にバックアップを取り、元に戻せる状態にしておきます。

キャッシュフィルターの仕組み

キャッシュフィルターは HTTP の標準的なキャッシュの規則(RFC 9111)に沿って動きます。つまり、Envoy が独自に「このページは何秒保存」と決めるのではなく、オリジンが返す Cache-Control などのヘッダーに従って 保存するかどうかと保存期間を決めます。

  • GET や HEAD のリクエストで、オリジンが Cache-Control: public, max-age=300 や s-maxage を返していれば、保存の対象になります。
  • no-store や private が付いたレスポンスは保存されません。
  • Vary ヘッダーを返すレスポンスは、許可された Vary ヘッダーの組み合わせでなければ保存されません。許可リストを指定する設定項目は、バージョンによって名称が変わっているため、利用中のバージョンの CacheConfig の説明を確認してください。

この性質から、Envoy 側の設定だけでなく、オリジン側で「何をどれだけキャッシュさせるか」をヘッダーで表明することが中心の作業になります。

手順1:最小構成の設定ファイルを書く

次は、ポート 10000 で受けて、後ろの WordPress(ホスト名 wordpress、ポート 80)へ転送し、その途中でキャッシュする最小構成の例です。Docker Compose などでサービス名が wordpress になっている環境を想定しています。

static_resources:
  listeners:
  - name: listener_http
    address:
      socket_address: { address: 0.0.0.0, port_value: 10000 }
    filter_chains:
    - filters:
      - name: envoy.filters.network.http_connection_manager
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
          stat_prefix: ingress_http
          route_config:
            name: local_route
            virtual_hosts:
            - name: wordpress
              domains: ["*"]
              routes:
              - match: { prefix: "/" }
                route: { cluster: wordpress }
          http_filters:
          - name: envoy.filters.http.cache
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.http.cache.v3.CacheConfig
              typed_config:
                "@type": type.googleapis.com/envoy.extensions.http.cache.simple_http_cache.v3.SimpleHttpCacheConfig
          - name: envoy.filters.http.router
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
  clusters:
  - name: wordpress
    type: STRICT_DNS
    connect_timeout: 2s
    load_assignment:
      cluster_name: wordpress
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address: { address: wordpress, port_value: 80 }

つまずきやすい点は2つあります。

1つ目は フィルターの順番 です。http_filters は上から順に処理され、router フィルターは最後に置く必要があります。キャッシュフィルターを router の後ろに書くと機能しません。

2つ目は @type の文字列 です。1文字でも違うと起動時にエラーになります。ストレージ実装の型名はバージョンで追加・変更があるため、コピーした例が動かないときは、利用中のバージョンのドキュメントにある型名と照らし合わせてください。

設定ファイルの文法は、起動前に検証モードで確認できます。

envoy --mode validate -c /etc/envoy/envoy.yaml

手順2:WordPress 側でキャッシュしてよいかをヘッダーで示す

WordPress は、表側のページに対して共有キャッシュ向けの Cache-Control を積極的には付けません。そのままでは Envoy がほとんど保存しないか、逆にプラグインやサーバー設定が付けたヘッダー次第で、意図しないページまで保存される可能性があります。

次の mu-plugin は、ログインしていない訪問者への GET リクエストだけに短時間の共有キャッシュを許可し、ログイン中のユーザーにはキャッシュ禁止のヘッダーを返す例です。wp-content/mu-plugins/edge-cache-headers.php として保存します(テーマに書くとテーマ変更で消えるため、mu-plugin にしています)。

<?php
/**
 * Plugin Name: Edge Cache Headers
 * Description: 共有キャッシュ(Envoy など)向けの Cache-Control を付ける。
 */

add_filter( 'wp_headers', function ( $headers ) {
	$method = isset( $_SERVER['REQUEST_METHOD'] ) ? strtoupper( sanitize_text_field( wp_unslash( $_SERVER['REQUEST_METHOD'] ) ) ) : 'GET';

	// ログイン中・コメント投稿者・GET 以外はキャッシュさせない
	$has_comment_cookie = false;
	foreach ( array_keys( $_COOKIE ) as $name ) {
		if ( 0 === strpos( $name, 'comment_author_' ) ) {
			$has_comment_cookie = true;
			break;
		}
	}

	if ( is_user_logged_in() || $has_comment_cookie || 'GET' !== $method ) {
		$headers = array_merge( $headers, wp_get_nocache_headers() );
		return $headers;
	}

	// 未ログインの GET:ブラウザには保存させず、共有キャッシュには 300 秒
	$headers['Cache-Control'] = 'public, max-age=0, s-maxage=300';
	return $headers;
} );

wp_headers フィルターは表側のリクエストで WP がヘッダーを送る直前に呼ばれます。この時点ではまだメインクエリが実行されていないため、is_404() や is_search() などの条件分岐タグは使えません。ページ種別ごとに保存期間を変えたい場合は、template_redirect アクションの中で header() を使う方法を検討してください。

管理画面(/wp-admin/)やログイン画面は WordPress 自体がキャッシュ禁止のヘッダーを返しますが、念のため次の手順で Envoy 側でも除外しておくと安全です。

手順3:管理画面とログイン中のリクエストをキャッシュから外す

ヘッダーの付け忘れや、プラグインが独自にヘッダーを付けるケースに備え、Envoy のルートでもキャッシュフィルターを無効にしておきます。ルートごとにフィルターを無効化するには typed_per_filter_config に envoy.config.route.v3.FilterConfig を指定し、disabled: true とします。手順1の routes を次のように置き換えます。

              routes:
              - match:
                  prefix: "/wp-admin"
                route: { cluster: wordpress }
                typed_per_filter_config:
                  envoy.filters.http.cache:
                    "@type": type.googleapis.com/envoy.config.route.v3.FilterConfig
                    disabled: true
              - match:
                  prefix: "/wp-login.php"
                route: { cluster: wordpress }
                typed_per_filter_config:
                  envoy.filters.http.cache:
                    "@type": type.googleapis.com/envoy.config.route.v3.FilterConfig
                    disabled: true
              - match:
                  prefix: "/"
                  headers:
                  - name: cookie
                    string_match: { contains: "wordpress_logged_in_" }
                route: { cluster: wordpress }
                typed_per_filter_config:
                  envoy.filters.http.cache:
                    "@type": type.googleapis.com/envoy.config.route.v3.FilterConfig
                    disabled: true
              - match: { prefix: "/" }
                route: { cluster: wordpress }

ルートは上から順に評価され、最初に一致したものが使われます。最後の汎用ルート(prefix "/")を一番下に置くのがポイントです。WooCommerce などカートを持つサイトでは、カート・決済・マイアカウントのパスや、カート関連の Cookie が付いたリクエストも同じ方法で除外します。対象のパスや Cookie 名はプラグインのバージョンや設定で変わるため、自分のサイトで実際の値を確認してから書いてください。

手順4:キャッシュが効いているか確認する

設定を反映したら、同じ URL に2回リクエストして、レスポンスヘッダーを比べます。

curl -s -o /dev/null -D - http://localhost:10000/sample-page/
curl -s -o /dev/null -D - http://localhost:10000/sample-page/

キャッシュから返ったレスポンスには、保存されてからの経過秒数を示す age ヘッダーが付くのが一般的です。2回目に age が付き、オリジンのアクセスログにリクエストが届いていなければ、キャッシュが効いています。

確認のポイントは次のとおりです。

  • 1回目のレスポンスの Cache-Control:public と s-maxage が付いているか。付いていなければ手順2のコードが動いていないか、別のプラグインが上書きしています。
  • Set-Cookie の有無:Cookie を発行するレスポンスは、保存すると他の訪問者に Cookie が配られる危険があります。セッションを作るプラグインがある場合、そのページは除外対象にします。
  • ログイン状態での確認:ブラウザでログインした状態のページに age が付いていないこと、別ブラウザ(未ログイン)にログイン中の表示が出ないことを確かめます。

Envoy の管理インターフェースを有効にしている場合は、統計情報(/stats)にキャッシュ関連の値が出ることがあります。項目名はバージョンで異なるため、/stats?filter=cache のように絞り込んで探してください。管理インターフェースは外部に公開しないよう、127.0.0.1 など内部のアドレスだけで待ち受けます。

運用上の注意

  • 記事を更新しても古いページが残る:簡易キャッシュには外部からの削除 API がないため、s-maxage を短め(数分程度)にして、更新の反映が遅れる時間を許容範囲に収めるのが基本です。即時反映が必要なら、パージ機能を持つ Varnish や CDN を併用します。
  • メモリ使用量:メモリ上のキャッシュは Envoy プロセスのメモリを使います。大きな画像や動画まで保存すると圧迫するため、静的ファイルはオリジンや CDN に任せる設計も検討してください。
  • 再起動で消える:メモリ上のキャッシュは Envoy の再起動で空になります。再起動直後はオリジンへの負荷が一時的に増えることを見込んでおきます。

まとめ

  • Envoy のキャッシュは HTTP キャッシュフィルター(envoy.filters.http.cache)に保存先の実装を指定して有効にし、router フィルターより前に置きます。
  • 保存するかどうかはオリジンの Cache-Control で決まるため、WordPress 側で未ログインの GET だけに s-maxage を付けるのが中心の作業です。
  • 管理画面・ログイン画面・ログイン Cookie 付きのリクエストは、FilterConfig の disabled でルート単位に除外します。
  • 2回の curl で age ヘッダーとオリジンのログを確認し、ログイン中の表示が他人に出ないことを確かめます。
  • 機能の成熟度はバージョンにより異なるため、パージや細かい制御が必要なら Varnish・Nginx・CDN との併用を検討します。

お気軽にご相談ください

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