Skip to content

ADR 023: PoC 段階における Vercel から ECS API への HTTPS 通信方針

  • Status: Accepted
  • Date: 2026-04-23
  • Deciders: Lead Engineer

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 呼び出しが必要。

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.tsrewrites を採用していたが、本番デプロイ時に 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 レンジが固定されないため)
apps/web/src/app/proxy/[...path]/route.ts
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) });
  • API 呼び出しは全て クライアントサイド(TanStack Query + Clerk useAuth() トークン)で行う。Server Component / Route Handler からの API 呼び出しは現状存在しないため、/proxy パスをクライアントでのみ使用する。
  • SSR から API を呼ぶケースが将来追加される場合は、サーバーサイドコードから process.env.API_URL(プライベート変数)を直接参照する。NEXT_PUBLIC_API_URL はクライアントバンドルに埋め込まれるため、本番の ALB URL はそちらに設定しない。

Route Handler はブラウザの Origin ヘッダーを ALB へ透過転送するため、ECS タスクの ALLOWED_ORIGINS に Vercel ドメインを含める必要がある(preflight ・ non-simple request が 403 になるため)。

独自ドメイン取得後、ALB に ACM 証明書 + HTTPS リスナーを設定し、プロキシを撤去する(クライアントから直接 ALB を呼ぶ構成に戻す)。

案 A: ALB に ACM 証明書 + HTTPS リスナーを追加(独自ドメイン必須)

Section titled “案 A: ALB に ACM 証明書 + HTTPS リスナーを追加(独自ドメイン必須)”

ALB に ACM で発行した証明書をアタッチし、443 ポートで HTTPS リスナーを設定する。

  • 標準的な構成。Vercel → ALB が直接 HTTPS で通信
  • ブラウザ → ALB の TLS 終端なので、Vercel に依存しない
  • パフォーマンス: プロキシのホップが減る
  • Phase 2 で本番運用するなら必須
  • 独自ドメインの取得・登録が必須(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 はそのまま当てはまる。

  • ロジックを実装側で柔軟に書ける(リクエスト変換、エラー整形等)
  • ヘッダー操作・キャッシュ制御を細かく制御可能
  • 全リクエストが 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 を指定する。

  • AWS 内で完結。Vercel に依存しない
  • CloudFront のデフォルトドメイン(*.cloudfront.net)に AWS 提供の証明書が自動付与されるため、独自ドメイン不要
  • WAF・DDoS 対策が容易
  • インフラ構成が複雑化(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 として動かす。

  • 同一ドメインで完結。CORS / Mixed Content 問題が発生しない
  • インフラが Vercel に一元化
  • 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 で配信する。

  • 何も追加しなくてよい
  • 現代のブラウザは HTTP サイトに警告を表示
  • Clerk 等のサードパーティが HTTPS 必須
  • 実用的でない
  • 追加インフラ不要で即座に動作(独自ドメイン・ACM 不要)
  • Next.js 標準機能のみで実現、Vercel デプロイにおける標準的なパターン
  • ローカル開発では NEXT_PUBLIC_API_PROXY フラグで OFF にでき、従来通り直接 API を呼べる
  • Phase 2 で ALB を HTTPS 化した際に rewrites を削除するだけで戻せる(撤去コスト低)
  • 全 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 値で OKNEXT_PUBLIC_API_PROXY=true の場合、client.tswindow.location.origin + "/proxy" を使うため)
  1. 独自ドメイン取得(Route 53 or 外部 DNS)
  2. ACM 証明書を発行(DNS 検証)
  3. ALB に HTTPS リスナー (443) を追加、証明書をアタッチ
  4. ALB のセキュリティグループを CloudFront / 既知 IP に絞る
  5. Vercel 環境変数 NEXT_PUBLIC_API_URLhttps://api.example.com に変更
  6. app/proxy/[...path]/route.ts を削除
  7. client.tsclientBaseUrl 分岐を削除