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

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

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

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

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

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

  2. レスポンスのステータス コードが 200、203、204、206、300、301、302、307、308、404、405、410、421、451、501 のいずれかである。

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

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

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

かつ、次のいずれにも該当しない場合:

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

  2. レスポンスに、Accept、Accept-Encoding、Access-Control-Request-Headers、Access-Control-Request-Method、Origin、Sec-Fetch-Dest、Sec-Fetch-Mode、Sec-Fetch-Site、X-Goog-Allowed-Resources、X-Origin、RSC、Next-Router-State-Tree、Next-Router-Prefetch、Next-Router-Segment-Prefetch、Next-Url 以外の値を持つ Vary ヘッダーがある。

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

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

  5. レスポンスに明示的なキャッシュ制御ディレクティブがない限り、リクエストには Authorization ヘッダーがあります。

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

Next.js

Next.js は、さまざまな要因に基づいて、cache-control ディレクティブを暗黙的に設定します。ただし、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 ディレクティブをデフォルトで設定しません。サーバー ルートで 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-age と s-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 で変換は適用されません。

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

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

ルートベースのモニタリングを使用して、ウェブアプリの特定のルートのキャッシュ ヒット率を表示することもできます。