【完全理解】CORSとは何か？React + FastAPIでの実践と本番ベストプラクティス解説！

## ✅ 概要

CORS（Cross-Origin Resource Sharing）は、**フロントエンドとバックエンドが異なるオリジンにある場合に避けて通れない重要なセキュリティ機構**です。本記事では、以下をフルスタックエンジニア視点で網羅的に解説します：
```mermaid
sequenceDiagram
    participant Browser as ブラウザ（フロントエンド）
    participant Server as サーバー（バックエンド API）

    Browser->>Server: GET /api/data (Origin: http://localhost:3000)
    Server-->>Browser: Access-Control-Allow-Origin ヘッダー付きのレスポンス
    Note right of Browser: ヘッダーを確認し、レスポンスを処理
```
```mermaid
sequenceDiagram
    actor Browser as ブラウザ（フロントエンド）
    participant Server as サーバー（バックエンド API）

    Browser->>Server: OPTIONS /api/data\nAccess-Control-Request-Method: PUT\nAccess-Control-Request-Headers: Content-Type\nOrigin: http://localhost:3000
    Server-->>Browser: 204 No Content\nAccess-Control-Allow-Origin: http://localhost:3000\nAccess-Control-Allow-Methods: PUT\nAccess-Control-Allow-Headers: Content-Type

    Browser->>Server: PUT /api/data\nOrigin: http://localhost:3000
    Server-->>Browser: 200 OK\nAccess-Control-Allow-Origin: http://localhost:3000\nデータレスポンス

```


* CORSの基礎知識と仕組み
* フロントエンド（JavaScript/React）とバックエンド（FastAPI）での実装例
* 開発時のCORS回避方法（Vite/CRA）
* 本番環境におけるCORSベストプラクティス
* セキュリティ上の注意点や罠

---

## 🔐 CORSとは？

### 🧩 オリジンとは？

オリジンは以下の3要素で定義されます：

| 要素   | 例                          |
| ---- | -------------------------- |
| スキーム | `http`, `https`            |
| ホスト  | `localhost`, `example.com` |
| ポート  | `3000`, `8000`             |

例えば：

* `http://localhost:3000`（Reactフロントエンド）
* `http://localhost:8000`（FastAPIバックエンド）

この2つは**異なるオリジン**です。

---

### 🤔 なぜCORSが必要？

ブラウザのセキュリティポリシー（同一オリジンポリシー：SOP）により、**異なるオリジンへのJavaScriptからのアクセスは禁止されています**。

ただし、フロントとバックが異なるオリジンで動作するのは一般的なので、CORSで**安全にアクセスを許可**する設定が必要になります。

---

## 🧪 React + FastAPI 実装例（開発環境）

### 🔧 バックエンド（FastAPI）

```python
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()

origins = ["http://localhost:3000"]

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

@app.get("/api/data")
def get_data():
    return {"message": "Hello from FastAPI!"}
```

---

### 🧑‍💻 フロントエンド（JavaScript）

```javascript
// fetch from frontend
async function fetchData() {
    const response = await fetch("http://localhost:8000/api/data", {
        method: "GET",
        headers: {
            "Content-Type": "application/json"
        }
    });

    const data = await response.json();
    console.log(data);
}

fetchData();
```

---

## 🛠 ReactでのCORS回避方法（開発時）

### 📦 方法1：Viteの場合

```js
// vite.config.js
export default {
  server: {
    proxy: {
      '/api': {
        target: 'http://localhost:8000',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, '')
      }
    }
  }
}
```

```js
fetch('/api/data'); // 実際には http://localhost:8000/data に転送
```

---

### 📦 方法2：Create React App（CRA）の場合

```json
// package.json
{
  "proxy": "http://localhost:8000"
}
```

```js
fetch('/api/data'); // 自動的にプロキシ経由でアクセスされる
```

---

## 🌍 本番環境でのCORS構成：ベストプラクティス

### ✅ 方法1：同一オリジン構成（静的ファイルをFastAPIで配信）

```python
from fastapi.staticfiles import StaticFiles

app.mount("/", StaticFiles(directory="frontend/dist", html=True), name="static")
```

この構成ではフロントとAPIが同じオリジンとなり、CORSの問題は**完全になくなります**。

---

### ✅ 方法2：異なるオリジンを使う場合

```python
app.add_middleware(
    CORSMiddleware,
    allow_origins=["https://your-frontend.com"],
    allow_credentials=True,
    allow_methods=["GET", "POST"],
    allow_headers=["Authorization", "Content-Type"],
)
```

* `allow_origins=["*"]` は本番では絶対に使わない
* `allow_credentials=True` の場合、オリジンはワイルドカード不可
* 認証系（JWT/Cookie）では `credentials: "include"` をJS側で指定

---

### ⚠ CORSと認証の罠（JWT, Cookie）

```js
// クッキー or JWT を利用する fetch の例
fetch("https://api.example.com/secure-data", {
  method: "GET",
  credentials: "include" // ← 必須！
});
```

```python
# FastAPI 側で credentials を許可
allow_credentials=True
```

---

## 🛡 セキュリティ上の注意点

| 項目             | 解説                                        |
| -------------- | ----------------------------------------- |
| `*` の使用        | 本番での `allow_origins=["*"]` は非推奨（認証無効化の危険） |
| HTTPS          | 本番では必須（セッション・認証情報が漏洩する危険）                 |
| allow\_headers | 認証に必要なヘッダー（例: Authorization）を忘れず許可        |

---

## ✅ まとめ

| フェーズ | 推奨構成                                        |
| ---- | ------------------------------------------- |
| 開発   | Vite/Craのプロキシ + FastAPIの緩いCORS設定            |
| 本番   | 同一オリジン構成（静的ファイルをAPIと同じドメインで配信） or CORSを明示設定 |