Language: 日本語
「なぜフロントとバックエンドの間に BFF を挟むのか?」 API Gateway との違いと、Cookie 回帰がもたらすセキュリティ境界
React や Next.js などで Web アプリケーションを開発するとき、**「フロントエンドから直接バックエンドの API を叩けば手っ取り早いのに、なぜわざわざ中間に BFF(Backend for Frontend)という層を挟むのか?」** と疑問に思ったことはありませんか?
React や Next.js などで Web アプリケーションを開発するとき、「フロントエンドから直接バックエンドの API を叩けば手っ取り早いのに、なぜわざわざ中間に BFF(Backend for Frontend)という層を挟むのか?」 と疑問に思ったことはありませんか?
「層を 1 つ増やすと保守やデプロイの手間が増えるだけでは?」という疑問はごく自然なものです。
しかし、BFF が生まれた歴史的な変遷と、現代の Web 開発における決定的な役割(UI 都合の分離と、トークン隔離による Cookie 回帰) を紐解くと、なぜ多くの現場で BFF が不可欠な設計パターンとして定着したのかがクリアに見えてきます。
この記事では、直接通信 ➔ API Gateway ➔ BFF へ至ったアーキテクチャの進化と、「ブラウザから生トークンを追放して HttpOnly Cookie に回帰する」セキュリティ境界としての BFF の本質 を解説します。
得られること
- 直接呼び出し ➔ API Gateway ➔ BFF へ至った進化の必然性と、それぞれの責任範囲の違いがわかる
- UI の変更頻度(激しい)とバックエンドの変更頻度(堅牢・安定)を分離する設計アプローチが学べる
- SPA の「localStorage + Bearer トークン」が抱えるセキュリティ危機と、BFF を通じた「安全な Cookie セッションへの回帰」の仕組みが理解できる!
BFF はどこから来たのか? 3 つの進化ステップ
BFF という概念が定着するまでには、フロントエンドとバックエンドの通信において 3 つの進化の段階がありました。
【 Phase 1: 直接通信 】
Client (Web / iOS) ──(複数回のリクエスト)──→ Backend Services
※ クライアント側でデータ合成とエラー制御を行い、コードが肥大化
【 Phase 2: API Gateway による集約 】
Client (Web / iOS) ────→ [ API Gateway (共通窓口) ] ────→ Backend Services
※ 全クライアントの要求が 1 箇所に集まり、Gateway が肥大化(God Gateway 化)
【 Phase 3: BFF によるクライアントごとの最適化 】
Web Client ────→ [ Web 専用 BFF ] ────┐
├────→ Backend Services
Mobile Client ────→ [ Mobile 専用 BFF ] ─┘
Phase 1: クライアントがバックエンドを直接呼ぶ構成
最初は、クライアントがバックエンドの各マイクロサービスを直接呼び出していました。
しかし、サービスが増えると 「1 つの画面を描くためにクライアントが 3〜4 回も API を叩く」 ことになります。 呼び出しの順序制御、一部が失敗したときのフォールバック、UI 向けのマッピング処理がすべてフロントエンド側のコードに乗り、画面側のコードが複雑怪奇に膨れ上がります。
さらに、「画面にこの項目も表示したい」という都合に合わせて、バックエンドの API に is_for_dashboard_v2 のような UI 専用の汚いパラメータが生え始め、API の汎用性が失われていきました。
Phase 2: API Gateway パターン(共通集約の限界)
この複雑さを解消するために登場したのが API Gateway です。
複数のバックエンドサービスの手前に 1 つの共通窓口を置き、認証、共通レート制限、ロギング、ルーティングを Gateway に集約しました。
しかし、ここで新たな壁にぶつかります。「Web とモバイル(iOS / Android)では、画面のレイアウトも必要なデータ構造もまったく異なる」 という点です。
全クライアントの細かな画面都合を 1 つの API Gateway に詰め込もうとした結果、Gateway はあらゆる要望を抱え込んだ「巨大な God Gateway」と化し、チーム間のデプロイが詰まるボトルネックになってしまいました。
Phase 3: BFF(Backend for Frontend)の誕生
そこで導き出されたのが 「クライアント(UI)ごとに専用のサーバー層(BFF)を用意する」 という BFF パターンです。
BFF はバックエンドではなく 「フロントエンドチームが主権を持つサーバー層」 として位置付けられます。 Web 用の BFF、モバイル用の BFF とクライアントごとに 1 対 1 で配置することで、バックエンドや共通 Gateway に一切迷惑をかけず、画面に最も都合の良いデータ整形・集約を自律的に行えるようになりました。
セキュリティ境界としての BFF: 生トークンの隔離と Cookie 回帰
BFF の価値は、単なる「データ整形プロキシ」にとどまりません。現代の Web において最も決定的なのが 「セキュリティ境界(Token Isolation)」としての役割 です。
SPA 黎明期の過ち: localStorage と Bearer トークンの罠
SPA(Single Page Application)が普及した当初、多くの現場で次のような認証構成が取られました。
- ブラウザが ID/Pass でログイン API を叩く
- レスポンスとして生 JWT(Access Token や Refresh Token)を受け取る
- ブラウザの
localStorageやメモリにトークンを保存する - 以降のリクエストで
Authorization: Bearer <JWT>ヘッダを付与して送信する
しかし、この構成には 「XSS(クロスサイトスクリプティング)の脆弱性が 1 箇所でもあると、悪意ある JavaScript から localStorage 内の生トークンが丸ごと盗まれる」 という致命的な危険があります。盗まれた Bearer トークンは、攻撃者の手元から自由に API を叩くために悪用されてしまいます。
BFF によるトークン隔離(Token Handler Pattern)
BFF を導入することで、このセキュリティ問題を根本から解決できます。
【 BFF によるトークン隔離と Cookie 回帰 】
ブラウザ (Client) BFF (Server) Backend API
│ │ │
│── 1. ログインリクエスト ───────────→│ │
│ │── 2. 認証・トークン発行 ──────→│
│ │←─ 3. 生 JWT / RefreshToken ──│
│←─ 4. HttpOnly / Secure Cookie ─────│ (※生トークンは BFF 内に隔離) │
│ (セッションIDのみ返却) │ │
│ │ │
│── 5. Cookie を付与してデータ要求 ──→│ │
│ │── 6. 生 JWT をヘッダに添えて ──→│
│ │←─ 7. レスポンス ───────────────│
│←─ 8. 画面用データを返却 ───────────│ │
- ブラウザ ↔ BFF 間:
HttpOnly,Secure,SameSite=Lax(または Strict)が付与された セッション Cookie だけ をやり取りします。- JavaScript からは Cookie の値を一切読み取れないため、仮に XSS が発生しても生トークンが外部に漏洩することはありません。
- BFF ↔ バックエンド間:
- 生の OAuth Access Token や Refresh Token、API Key は BFF のサーバーメモリや安全なセッションストア(Redis 等)に完全隔離 されます。
- バックエンド API を呼び出す際、BFF が代理で
Authorization: Bearer <Token>を付与して通信します。
近年、徳丸浩氏や多くのセキュリティエンジニアが提唱している通り、「ブラウザ側で生 JWT を抱える時代は終わり、BFF を通じた安全な Cookie セッションへ回帰する」 のが現代の堅牢な標準アーキテクチャとなっています。
責任の 3 層分離: 何をどこに置くか?
BFF を導入する際、最も大切なのは 「ドメインロジックを BFF に抱え込ませない」 という境界線です。
| 層 | 主な責務 | 変更頻度 |
|---|---|---|
| フロントエンド (UI) | 画面描画、ユーザー操作のハンドリング、ローカル状態管理 | 極めて高い(日単位) |
| BFF | 画面専用のデータ整形・集約、トークン隔離(Cookie ↔ JWT 変換)、UI 認可 | 極めて高い(UI と同期) |
| バックエンド (API) | ビジネスロジック、データ整合性、トランザクション、永続化 | 低い・安定的(堅牢性重視) |
BFF に置いてよいもの
- 複数のマイクロサービスからデータを集めて 1 つの画面用 JSON に束ねる処理
- 画面固有のデータ変換(日付フォーマット、不要フィールドの削ぎ落とし)
- 認証トークンの管理と Cookie への変換
- A/B テストの振り分けや UI 向けフィーチャーフラグ判定
バックエンドに寄せるべきもの
- 金額計算や決済、ステータス遷移などの ビジネスルール・ドメインロジック
- データベースへのトランザクションと整合性の担保
- 重い集計・バッチ処理
もしビジネスロジックを BFF に書いてしまうと、モバイル用 BFF や他のバックエンドとの間で二重管理になり、不整合の温床になります。「BFF は UI に寄り添うデータ整形係であり、ドメインの真実はバックエンドが握る」 という分離を徹底することが成功の秘訣です。
まとめ: BFF は「変更頻度の境界」であり「セキュリティの防護壁」
BFF は、単に API を中継するプロキシではありません。
- 変更頻度の分離: 画面都合の目まぐるしい変更からバックエンドの安定性を守る
- データ合成の最適化: モバイルや Web の通信回数を 1 回に絞り、表示速度を向上させる
- セキュリティの防護壁: 生トークンをサーバー側に隔離し、安全な Cookie セッションへ回帰させる
この 3 つの価値を正しく理解して境界を引くことで、フロントエンドとバックエンドの双方が高い開発速度と堅牢性を両立できるようになります。
では、この BFF を実際に構築する際、「変更頻度の高い BFF をいかに最小の実装コストと E2E 型安全で立ち上げるか?」。 その具体的な技術選定と比較については、次の記事 『BFF を最小コストで始めるなら tRPC』 で詳しく解説します!