ADR 023: PoC 段階における Vercel から ECS API への HTTPS 通信方針
- Status: Accepted
- Date: 2026-04-23
- Deciders: Lead Engineer
Context
Section titled “Context”Vercel にデプロイしたフロントエンド(HTTPS)から ECS Fargate 上の API(ALB の HTTP エンドポイント)を呼び出す構成において、ブラウザの Mixed Content ポリシーにより、HTTPS ページから HTTP リソースへのリクエストがブロックされる。
現状の構成:
- フロントエンド: Vercel にデプロイ(
https://<vercel-domain>.vercel.app) - API: ECS Fargate + ALB(
http://<alb-dns>.elb.amazonaws.com) - ALB: HTTP リスナー (Port 80) のみ。ACM 証明書未設定、ドメイン未取得
PoC 段階のため、独自ドメイン取得や ACM 証明書管理に時間をかけたくないが、Vercel 上で動作するサイトから API 呼び出しが必要。
Decision
Section titled “Decision”PoC 段階では Next.js Route Handler (app/proxy/[...path]/route.ts) による Vercel サーバーサイドプロキシ を採用する。
Update (2026-04-23): rewrites から Route Handler へ切替
Section titled “Update (2026-04-23): rewrites から Route Handler へ切替”当初は next.config.ts の rewrites を採用していたが、本番デプロイ時に Vercel が x-vercel-error: DNS_HOSTNAME_RESOLVED_PRIVATE を返してプロキシが動作しないことを確認した(ALB DNS は public IP のみに解決されるが、Vercel の rewrites 側で SSRF 保護として *.elb.amazonaws.com 等のパターンが弾かれる挙動)。
Server Function (Route Handler) 内の fetch() には同制約が無いため、実装方法を Route Handler に切り替えた。判断 (Vercel サーバーサイドプロキシで HTTPS 化) は同じで、実装手段のみ変更。これに伴い当初の代替案 B (Route Handler) は実質採用案となった。
- フロントエンドのブラウザ → Vercel (
https://.../proxy/*) → ALB (http://.../...) - Route Handler はサーバーサイドで実行されるため、ブラウザは同一オリジン (HTTPS) への通信のみ
- ブラウザ → Vercel 間は HTTPS で暗号化、Vercel → ALB 間は AWS リージョン外通信のため最小限のリスクとして受容
- ALB のセキュリティグループは PoC 段階では
0.0.0.0/0を許可(Vercel の IP レンジが固定されないため)
const upstream = new URL(`/${path.join("/")}${req.nextUrl.search}`, process.env.API_URL);const upstreamRes = await fetch(upstream, { method: req.method, headers: filterHeaders(req.headers), // hop-by-hop ヘッダー除外 body: hasBody ? req.body : undefined, duplex: hasBody ? "half" : undefined, redirect: "manual",});return new Response(upstreamRes.body, { status, headers: filterHeaders(upstreamRes.headers) });前提・適用範囲
Section titled “前提・適用範囲”- API 呼び出しは全て クライアントサイド(TanStack Query + Clerk
useAuth()トークン)で行う。Server Component / Route Handler からの API 呼び出しは現状存在しないため、/proxyパスをクライアントでのみ使用する。 - SSR から API を呼ぶケースが将来追加される場合は、サーバーサイドコードから
process.env.API_URL(プライベート変数)を直接参照する。NEXT_PUBLIC_API_URLはクライアントバンドルに埋め込まれるため、本番の ALB URL はそちらに設定しない。
API 側(ECS)の CORS 設定
Section titled “API 側(ECS)の CORS 設定”Route Handler はブラウザの Origin ヘッダーを ALB へ透過転送するため、ECS タスクの ALLOWED_ORIGINS に Vercel ドメインを含める必要がある(preflight ・ non-simple request が 403 になるため)。
Phase 2 移行時
Section titled “Phase 2 移行時”独自ドメイン取得後、ALB に ACM 証明書 + HTTPS リスナーを設定し、プロキシを撤去する(クライアントから直接 ALB を呼ぶ構成に戻す)。
Alternatives Considered
Section titled “Alternatives Considered”案 A: ALB に ACM 証明書 + HTTPS リスナーを追加(独自ドメイン必須)
Section titled “案 A: ALB に ACM 証明書 + HTTPS リスナーを追加(独自ドメイン必須)”ALB に ACM で発行した証明書をアタッチし、443 ポートで HTTPS リスナーを設定する。
Positive
Section titled “Positive”- 標準的な構成。Vercel → ALB が直接 HTTPS で通信
- ブラウザ → ALB の TLS 終端なので、Vercel に依存しない
- パフォーマンス: プロキシのホップが減る
- Phase 2 で本番運用するなら必須
Negative
Section titled “Negative”- 独自ドメインの取得・登録が必須(ACM の DNS 検証に必要)
- ALB のデフォルト DNS 名(
*.elb.amazonaws.com)には ACM 証明書を発行できない - DNS 管理(Route 53 or 外部 DNS)の Terraform 化が必要
- PoC 段階の「動かす」優先度には重い
案 B: Vercel API Routes (Route Handler) でプロキシ
Section titled “案 B: Vercel API Routes (Route Handler) でプロキシ”Next.js の app/api/proxy/[...path]/route.ts を実装し、fetch() で ALB に転送する。
2026-04-23 補足: 当初採用した
rewritesが SSRF 保護で動作しなかったため、本案 B が実際の採用案となった。下記の Positive/Negative はそのまま当てはまる。
Positive
Section titled “Positive”- ロジックを実装側で柔軟に書ける(リクエスト変換、エラー整形等)
- ヘッダー操作・キャッシュ制御を細かく制御可能
Negative
Section titled “Negative”- 全リクエストが Vercel のサーバーレス関数を経由するため、コールドスタート分のレイテンシが発生
- Vercel Hobby Plan の関数実行時間制限(10 秒)に注意が必要
- 実装コードが増える(テスト対象も増える)
- Hono RPC クライアントの型情報を別途維持する必要が出る
案 C: CloudFront + ACM で ALB 前段を HTTPS 化
Section titled “案 C: CloudFront + ACM で ALB 前段を HTTPS 化”CloudFront をフロントエンドに置き、ACM 証明書で HTTPS 終端。Origin として ALB の HTTP を指定する。
Positive
Section titled “Positive”- AWS 内で完結。Vercel に依存しない
- CloudFront のデフォルトドメイン(
*.cloudfront.net)に AWS 提供の証明書が自動付与されるため、独自ドメイン不要 - WAF・DDoS 対策が容易
Negative
Section titled “Negative”- インフラ構成が複雑化(CloudFront の Terraform 追加、キャッシュ設定、Origin 設定)
- API 用途では CloudFront のキャッシュを基本無効化するため、メリットが限定的
- Vercel → CloudFront → ALB と 2 段プロキシになり、レイテンシ増
- PoC のスコープを超える
案 D: Vercel と同じドメインで API を提供(API も Vercel にデプロイ)
Section titled “案 D: Vercel と同じドメインで API を提供(API も Vercel にデプロイ)”Hono API を Vercel の Edge Functions / Serverless Functions として動かす。
Positive
Section titled “Positive”- 同一ドメインで完結。CORS / Mixed Content 問題が発生しない
- インフラが Vercel に一元化
Negative
Section titled “Negative”- ECS Fargate + Aurora の構成(ADR 003)を捨てることになる
- VPC 内の Aurora にプライベート接続できなくなる(Vercel から Aurora への RDS Proxy 経由など別の課題が発生)
- PoC で確認したい「ECS + Aurora」アーキテクチャの検証ができなくなる
案 E: ALB を HTTPS 化せず、フロントエンドも HTTP で公開
Section titled “案 E: ALB を HTTPS 化せず、フロントエンドも HTTP で公開”Vercel ではなく ALB の前に CloudFront 等を置かず、フロントエンドも HTTP で配信する。
Positive
Section titled “Positive”- 何も追加しなくてよい
Negative
Section titled “Negative”- 現代のブラウザは HTTP サイトに警告を表示
- Clerk 等のサードパーティが HTTPS 必須
- 実用的でない
Consequences
Section titled “Consequences”Positive
Section titled “Positive”- 追加インフラ不要で即座に動作(独自ドメイン・ACM 不要)
- Next.js 標準機能のみで実現、Vercel デプロイにおける標準的なパターン
- ローカル開発では
NEXT_PUBLIC_API_PROXYフラグで OFF にでき、従来通り直接 API を呼べる - Phase 2 で ALB を HTTPS 化した際に
rewritesを削除するだけで戻せる(撤去コスト低)
Negative
Section titled “Negative”- 全 API リクエストが Vercel のサーバーレス Edge を経由するため、レイテンシが増加(数十 ms 程度)
- Vercel → ALB 間が HTTP のため、AWS リージョン外(Vercel のエッジ)からの通信が暗号化されない
- PoC 段階の暫定対応として受容。本番運用時は案 A または C で HTTPS 化必須
- ALB のセキュリティグループを
0.0.0.0/0で開放する必要がある(Vercel の Outbound IP は固定されない)- Vercel Enterprise の Static IP 機能、または案 A/C へ移行することで Phase 2 で解消
API_URL(Route Handler が upstream として参照、サーバーサイド専用)と、NEXT_PUBLIC_API_PROXY=true(クライアントに/proxyを使わせるフラグ)の 2 つを Vercel に設定する必要がある- 本番では
NEXT_PUBLIC_API_URLは 未設定 or ローカル fallback 値で OK(NEXT_PUBLIC_API_PROXY=trueの場合、client.tsはwindow.location.origin + "/proxy"を使うため)
- 本番では
Migration Plan (Phase 2)
Section titled “Migration Plan (Phase 2)”- 独自ドメイン取得(Route 53 or 外部 DNS)
- ACM 証明書を発行(DNS 検証)
- ALB に HTTPS リスナー (443) を追加、証明書をアタッチ
- ALB のセキュリティグループを CloudFront / 既知 IP に絞る
- Vercel 環境変数
NEXT_PUBLIC_API_URLをhttps://api.example.comに変更 app/proxy/[...path]/route.tsを削除client.tsのclientBaseUrl分岐を削除