Firebase Hosting + Cloud Run で認証が動かない?Cookie名を __session にすべき理由

TL;DR

Firebase Hosting 経由で Cloud Run にアクセスする場合、セッション Cookie の名前は 必ず __session にする必要がある。それ以外の名前(session, auth, token など)は Firebase Hosting (CDN) によってストリップ(削除)され、バックエンドに届かない。

問題の症状

Next.js アプリを Firebase Hosting + Cloud Run で運用している際、以下のような不可解な現象に遭遇した:

  • ✅ Cloud Run の URL(https://<service>-<hash>.run.app)では正常にログインできる
  • ❌ Firebase Hosting の URL(https://<project>.web.app)ではログインできない
  • Set-Cookie ヘッダーは正しく返されている
  • ブラウザの Application タブでは Cookie が設定されている
  • しかし Middleware では Cookie が undefined になる

Firebase Hosting は CDN として動作する

Firebase Hosting は静的コンテンツ配信のために CDN(コンテンツ配信ネットワーク)として動作する。CDN はキャッシュ効率を最大化するため、デフォルトで全ての Cookie をストリップ(削除)する

ブラウザ → Firebase Hosting (CDN) → Cloud Run (バックエンド)
           ↑ここで Cookie がストリップされる

__session だけが例外

Firebase Hosting は __session という名前の Cookie だけは例外的にバックエンドに転送するように設計されている。これは Firebase の公式仕様で、認証セッションを扱うための特別なルールである。

Firebase Hosting の Cookie ルール:

  • __session で始まる Cookie のみバックエンドに転送される
  • その他の Cookie は全てキャッシュ効率化のためストリップされる
  • 最大 Cookie サイズは 4KB

参考: Firebase Hosting Documentation

なぜ Cloud Run URL では動いていたのか?

Cloud Run の URL に直接アクセスする場合は、Firebase Hosting (CDN) を経由しないため、Cookie がストリップされずにバックエンドに届く。

# Firebase Hosting 経由(CDN あり)
ブラウザ → Firebase Hosting → Cloud Run
          ↑Cookie がストリップされる

# Cloud Run 直接(CDN なし)
ブラウザ → Cloud Run
          ↑Cookie がそのまま届く
環境Cookie 名動作理由
Cloud Run URL 直接session✅ 動くCDN を経由しない
Firebase Hosting URLsession❌ 動かないCDN が Cookie をストリップ
Firebase Hosting URL__session✅ 動くCDN が __session だけは転送

解決方法

変更前:

// ❌ Firebase Hosting でストリップされる
const COOKIE_NAME = 'session';
cookieStore.set('session', sessionCookie, {
  httpOnly: true,
  secure: true,
  sameSite: 'lax',
  path: '/',
});

変更後:

// ✅ Firebase Hosting を通過する
const COOKIE_NAME = '__session';
cookieStore.set('__session', sessionCookie, {
  httpOnly: true,
  secure: true,
  sameSite: 'lax',
  path: '/',
});

2. Middleware も更新

変更前:

// ❌ Firebase Hosting 経由では undefined になる
const sessionCookie = request.cookies.get('session');

変更後:

// ✅ Firebase Hosting 経由でも取得できる
const sessionCookie = request.cookies.get('__session');

3. テストも更新

テストで Cookie 名を直接参照している場合も更新が必要:

// 変更前
expect(mockCookieStore.set).toHaveBeenCalledWith('session', ...);

// 変更後
expect(mockCookieStore.set).toHaveBeenCalledWith('__session', ...);

問題の診断方法

Firebase Hosting + Cloud Run 構成で認証問題が発生した場合、以下の手順で診断できる:

ブラウザの DevTools → Network タブで、Set-Cookie ヘッダーが返されているか確認:

Set-Cookie: session=eyJhbGc...; Path=/; HttpOnly; Secure; SameSite=Lax

✅ ヘッダーが返されている → Cookie 設定は成功

DevTools → Application タブ → Cookies で Cookie が保存されているか確認:

✅ Cookie が保存されている → ブラウザは Cookie を受け取っている

3. Middleware でのログ確認

Middleware に一時的なログを追加:

export async function middleware(request: NextRequest) {
  const sessionCookie = request.cookies.get('session');
  console.log('Cookie value:', sessionCookie?.value);
  // ...
}

undefined が出力される → Firebase Hosting で Cookie がストリップされている

4. Cloud Run URL で直接テスト

Firebase Hosting を経由せず、Cloud Run の URL に直接アクセスしてテスト:

# Cloud Run URL を取得
gcloud run services describe <service-name> \
  --region=<region> \
  --format='value(status.url)'

# ブラウザで直接アクセス
https://<service>-<hash>.run.app

✅ Cloud Run URL では動く → 問題は Firebase Hosting の Cookie 制限

まとめ

Firebase Hosting + Cloud Run 構成でセッション Cookie を使用する場合:

  1. Cookie 名は必ず __session にする
  2. Firebase Hosting は CDN として動作し、__session 以外の Cookie をストリップする
  3. Cloud Run URL では動くのに Firebase Hosting URL では動かない場合、この問題を疑う
  4. Cookie サイズは 4KB 以内に収める

この制限は Firebase Hosting の仕様であり、回避することはできない。Firebase + Cloud Run でセッション認証を実装する場合は、最初から __session という名前を使用することを推奨する。

参考資料