【完全保存版】CloudFront × Angular × Django API[403 を完全解消する正しい API 呼び出し方法と実装例まとめ]

 

403 を完全解消する正しい API 呼び出し方法と実装例まとめ

Angular + CloudFront + ALB + Django という構成で SPA を構築していると、
**「API を呼ぶと 403 Forbidden が返る」**という問題が高確率で発生します。

結論から言うと、この 403 は **設定ミスではなく、あなたの構成上“正しい挙動”**です。

本記事では、

  • なぜ 403 が発生するのか(重要)

  • なぜ CloudFront を経由すべきなのか

  • Angular 側で API URL をどう書くべきか

  • dev/stg/prod 環境ごとの API 切り替え方法

  • 実際の Angular 画面(トップ/管理画面)コピペ実装

これらを分かりやすくまとめます。



❌ まず結論:403 は「正しい」動作でした

あなたの ALB(API) + WAF は、次のような Origin Header 認証方式を採用しています。

CloudFront → x-origin-auth → ALB → WAF → Django

CloudFront から来た場合だけ、
x-origin-auth: <secret> という秘密ヘッダが付与されます。

しかし…


❌ Angular が API を直接叩くとヘッダが無いため必ず 403

例:

https://api.example.com/api/ping/

これは Angular から ALB(API) に直接アクセスしているため
秘密ヘッダが付かず、WAF に Block されます。


🔥 なぜ直接呼ぶと必ず 403 になるのか?

あなたの API 周りのセキュリティはこうです:

CloudFront 以外からのアクセス → 全て 403

CloudFront からのアクセス:

  • x-origin-auth ヘッダ付与される → 許可

ブラウザや curl や Postman:

  • ヘッダが付かない → 403 → 正常


✔ 403 を解消する唯一の方法

→ API を CloudFront 経由で呼ぶこと

つまり Angular の API 呼び出し URL をこうします:

/api/ping/

完全な URL が:

https://example.com/api/ping/

すると CloudFront が判定し:

PathPattern: api/*
↓
API Origin に転送(x-origin-auth を付与)
↓
ALB
↓
Django

→ 403 が完全に解消されます。


📌 403 が消える仕組みを図にするとこう

Angular (example.com)
       ↓
CloudFront
       ↓ (x-origin-auth を付与)
ALB(API)
       ↓
WAF 通過
       ↓
Django

🔍 逆にこうすると必ず 403

Angular → https://api.example.com/api/ping/
(CloudFrontを経由しない)
→ ヘッダなし
→ WAF Block(403)

これは セキュリティ的に正しい挙動です。



🧩 Angular 実装:API を CloudFront 経由で呼ぶ修正版

✔ API サービス(api.service.ts)

return this.http.get('/api/ping/');

絶対に直接の完全URL(https://api.example.com/...)は使わない。



🌎 Angular の環境ファイルで dev/stg/prod を切り替える方法(重要)

🔧 environment.dev.ts

export const environment = {
  production: false,
  apiBaseUrl: '/api', // CloudFront 経由で API を呼ぶ
  baseUrl: 'https://csirt.dev.example.com'
};

🔧 environment.stg.ts

export const environment = {
  production: false,
  apiBaseUrl: '/api',
  baseUrl: 'https://csirt.stg.example.com'
};

🔧 environment.prod.ts

export const environment = {
  production: true,
  apiBaseUrl: '/api',
  baseUrl: 'https://csirt.prod.example.com'
};

✔ Angular.json のビルド設定に環境を追加

"configurations": {
  "production": {
    "fileReplacements": [
      {
        "replace": "src/environments/environment.ts",
        "with": "src/environments/environment.prod.ts"
      }
    ]
  },
  "stg": {
    "fileReplacements": [
      {
        "replace": "src/environments/environment.ts",
        "with": "src/environments/environment.stg.ts"
      }
    ]
  },
  "dev": {
    "fileReplacements": [
      {
        "replace": "src/environments/environment.ts",
        "with": "src/environments/environment.dev.ts"
      }
    ]
  }
}

各環境のビルド:

ng build --configuration=dev
ng build --configuration=stg
ng build --configuration=production

✔ API 呼び出しはこう書く(CloudFront経由)

this.http.get(environment.apiBaseUrl + '/ping/');


🎨 画面実装:トップページと管理画面(コピペOK)

✔ Routing(app-routing.module.ts)

const routes: Routes = [
  { path: 'top', component: TopComponent },
  { path: 'admin', component: AdminComponent },
  { path: '', redirectTo: 'top', pathMatch: 'full' },
  { path: '**', redirectTo: 'top' }
];

✔ トップ画面

top.component.html

<h1>Top Page</h1>
<a routerLink="/admin">Go to Admin Page</a>

✔ 管理画面

admin.component.ts

callPing() {
  this.api.ping().subscribe({
    next: (res) => this.result = JSON.stringify(res),
    error: (err) => this.result = 'Error: ' + JSON.stringify(err)
  });
}


🔍 動作確認

成功ルート:

https://example.com/api/ping/  → 200

失敗ルート:

https://api.example.com/api/ping/ → 403(正常)

🎯 最終まとめ

動作 結果
Angular → CloudFront → API ✔ 200
Angular → API 直接 ❌ 403
CloudFront 経由の場合だけ OriginAuth ヘッダが付与 ✔ 200
dev/stg/prod の API 切替は environment.ts で管理 ✔ 安定

🚀 必要であれば追加記事も作成します

  • dev/stg/prod 向け CloudFront × S3 × API × Route53 まとめ記事

  • ECS Fargate CI/CD(GitHub Actions)

  • Angular の SPA キャッシュ制御

  • Django API のセキュリティ強化(JWT/Cognito統合)

どれを追記しましょうか?