アプリのコンテンツをキャッシュに保存する

Cloud CDN は、App Hosting によるウェブアプリのサポートの重要な部分です。バックエンドへのすべてのリクエストは、まず Cloud CDN を経由します。CDN にすでにキャッシュに保存されているコンテンツは、ウェブアプリのサーバーコードを実行している Cloud Run サービスへのアクセスをスキップして、ユーザーにすぐに提供されます。CDN の一般的なメリットについては、web.dev をご覧ください。

基本的な Cloud CDN 構成は App Hosting によって設定され、変更できませんが、キャッシュを最適化してページの読み込み速度を向上させ、キャッシュに保存されていないコンテンツの課金を減らし、Cloud Run へのトラフィックを最小限に抑えるために、さまざまな方法があります。

キャッシュに保存可能なコンテンツ

次の条件がすべて true の場合、Cloud CDN はレスポンスをキャッシュに保存します。

  1. リクエストが GET である

  2. レスポンスのステータス コードが 200203204206300301302307308404405410421451501 のいずれかである。

  3. レスポンスには、max-age または s-maxage ディレクティブを含む Cache-Control ヘッダー、あるいは将来のタイムスタンプを含む Expires ヘッダーがあります。

  4. レスポンスに Age ヘッダーまたは明示的な public ディレクティブを含む Cache-Control ヘッダーがある。

  5. レスポンスのサイズが 10 MiB 以下である。

次のいずれも該当しない。

  1. レスポンスに Set-Cookie ヘッダーがある

  2. レスポンスの Vary ヘッダーに、AcceptAccept-EncodingAccess-Control-Request-HeadersAccess-Control-Request-MethodOriginSec-Fetch-DestSec-Fetch-ModeSec-Fetch-SiteX-Goog-Allowed-ResourcesX-OriginRSCNext-Router-State-TreeNext-Router-PrefetchNext-Router-Segment-Prefetch 以外の値が設定されている。

  3. レスポンスには、no-store または private ディレクティブを含む Cache-Control ヘッダーがあります。

  4. リクエストに no-store ディレクティブを含む Cache-Control ヘッダーがある。

  5. レスポンスに明示的なキャッシュ制御ディレクティブがない場合を除き、リクエストに Authorization ヘッダーがある。

キャッシュ制御ディレクティブで動作をカスタマイズする

Next.js

Next.js は、さまざまな要因に基づいて、キャッシュ制御ディレクティブを暗黙的に設定します。ただし、next.config.js ファイルでヘッダーを設定することで、これらの値をオーバーライドできます。たとえば、ページが Cloud CDN にキャッシュに保存されないようにするには、次のようにします。

  /** @type {import('next').NextConfig} */
  const nextConfig = {
      headers: async () => [{
          source: "/YOUR_PRIVATE_PAGE",
          headers: [{
              key: "Cache-Control",
              value: "private"
          }],
      }],
  };

Angular

Angular SSR では、明示的な cache-control ディレクティブは設定されていません。独自のヘッダーを追加するには、サーバー ルートでキャッシュ制御ヘッダーを指定します。たとえば、Cloud CDN がすべてのページを 1 時間キャッシュに保存できるようにするには、次のようにします。

import { RenderMode, ServerRoute } from '@angular/ssr';

export const serverRoutes: ServerRoute[] = [
  {
    path: '**',
    renderMode: RenderMode.Prerender,
    headers: {
      'Cache-Control': 'public, max-age=3600',
    }
  }
];

特定のページがキャッシュに保存されないようにするには:

import { RenderMode, ServerRoute } from '@angular/ssr';

export const serverRoutes: ServerRoute[] = [
  // ... other routes
  {
    path: 'YOUR_PRIVATE_PAGE',
    renderMode: RenderMode.Server,
    headers: {
      'Cache-Control': 'private',
    }
  }
];

尊重されるディレクティブ

Firebase App Hosting の Cloud CDN インスタンスは、次のキャッシュ制御ディレクティブを遵守します。

ディレクティブ リクエスト レスポンス
no-store リクエストに含まれている場合、レスポンスはキャッシュに保存されません。 no-store を含むレスポンスはキャッシュに保存されません。
no-cache no-cache リクエスト ディレクティブは無視され、クライアントが送信元に対する再検証を開始または強制する可能性が回避されます。 no-cache を含むレスポンスはキャッシュに保存されますが、配信前に送信元で再検証する必要があります。
public なし このディレクティブはキャッシュへの保存に必須ではありませんが、プロキシによってキャッシュに保存されるコンテンツには、このディレクティブを含めることをおすすめします。
private なし private ディレクティブを含むレスポンスは、レスポンスがその他の理由でキャッシュ保存可能であるとみなされる場合でも、Cloud CDN によってキャッシュに保存されません。クライアント(ブラウザなど)は、引き続き結果をキャッシュに保存する可能性があります。レスポンスのキャッシュへの保存をすべて回避するには、no-store を使用します。
max-age=SECONDS max-age リクエスト ディレクティブは無視されます。キャッシュに保存されたレスポンスは、このヘッダーがリクエストに含まれていない場合と同様に返されます。 max-age ディレクティブを含むレスポンスは、最大で SECONDS までキャッシュに保存されます。
s-maxage=SECONDS なし s-maxage ディレクティブを含むレスポンスは、最大で SECONDS までキャッシュに保存されます。max-ages-maxage の両方が存在する場合、s‑maxage が Cloud CDN で使用されます。このディレクティブを含む古くなったレスポンスは配信されません。s-max-age(2 つのハイフン)は、キャッシュを保存する目的に対しては無効です。
max-stale=SECONDS max-stale リクエスト ディレクティブは、クライアントが受け入れ可能な最大ステイルネス(秒単位)を指定します。Cloud CDN はこれを適用し、レスポンスのステイルネスが max-stale ディレクティブより小さい場合にのみ、古いキャッシュ レスポンスを返します。それ以外の場合は、リクエストのサービスを提供する前に再検証します。 なし
stale-while-revalidate=SECONDS なし 再検証が非同期で行われている間、stale-while-revalidate を含むレスポンスは最大で SECONDS の間クライアントに配信されます。
must-revalidate なし must-revalidate を含むレスポンスは、有効期限が経過した後に送信元サーバーで再検証されます。このディレクティブを含む古くなったレスポンスは配信されません。
proxy-revalidate proxy-revalidate を含むレスポンスは、有効期限が経過した後に送信元サーバーで再検証されます。このディレクティブを含む古くなったレスポンスは配信されません。
no-transform なし Cloud CDN で変換は適用されません。

キャッシュに保存されたトラフィックとキャッシュに保存されていないトラフィックを測定する

App Hosting コンソールの [使用状況] タブの [Cloud CDN - アウトバウンド 帯域幅] グラフには、キャッシュに保存されたバイトとキャッシュに保存されていないバイト数が表示され、ロールアウトごとにマークが付けられます。このグラフを使用して、キャッシュの最適化の効果を測定できます。