0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Last updated at Posted at 2025-05-28

✅ 概要

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を明示設定
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?