✅ 概要
CORS(Cross-Origin Resource Sharing)は、フロントエンドとバックエンドが異なるオリジンにある場合に避けて通れない重要なセキュリティ機構です。本記事では、以下をフルスタックエンジニア視点で網羅的に解説します:
- 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)
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)
// 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の場合
// vite.config.js
export default {
server: {
proxy: {
'/api': {
target: 'http://localhost:8000',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
}
}
fetch('/api/data'); // 実際には http://localhost:8000/data に転送
📦 方法2:Create React App(CRA)の場合
// package.json
{
"proxy": "http://localhost:8000"
}
fetch('/api/data'); // 自動的にプロキシ経由でアクセスされる
🌍 本番環境でのCORS構成:ベストプラクティス
✅ 方法1:同一オリジン構成(静的ファイルをFastAPIで配信)
from fastapi.staticfiles import StaticFiles
app.mount("/", StaticFiles(directory="frontend/dist", html=True), name="static")
この構成ではフロントとAPIが同じオリジンとなり、CORSの問題は完全になくなります。
✅ 方法2:異なるオリジンを使う場合
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)
// クッキー or JWT を利用する fetch の例
fetch("https://api.example.com/secure-data", {
method: "GET",
credentials: "include" // ← 必須!
});
# FastAPI 側で credentials を許可
allow_credentials=True
🛡 セキュリティ上の注意点
項目 | 解説 |
---|---|
* の使用 |
本番での allow_origins=["*"] は非推奨(認証無効化の危険) |
HTTPS | 本番では必須(セッション・認証情報が漏洩する危険) |
allow_headers | 認証に必要なヘッダー(例: Authorization)を忘れず許可 |
✅ まとめ
フェーズ | 推奨構成 |
---|---|
開発 | Vite/Craのプロキシ + FastAPIの緩いCORS設定 |
本番 | 同一オリジン構成(静的ファイルをAPIと同じドメインで配信) or CORSを明示設定 |