はい、承知いたしました。FastAPIにおけるCORS設定について、設定方法からエラー対応までを網羅した約5000語の詳細な解説記事を作成します。
FastAPI CORS 完全ガイド:設定からエラー対応まで
はじめに:なぜCORSが必要なのか?
現代のWebアプリケーションは、多くの場合、ブラウザで実行されるフロントエンド(HTML, CSS, JavaScript)と、サーバーで実行されるバックエンドAPIで構成されています。これらのフロントエンドとバックエンドが異なるドメイン、ポート、またはプロトコルで動作している場合、ブラウザのセキュリティ機能によって、あるオリジン(源)から別のオリジンへのHTTPリクエストが制限されます。このセキュリティメカニズムを「同一オリジンポリシー (Same-Origin Policy – SOP)」と呼びます。
SOPは、悪意のあるウェブサイトが、ユーザーがログインしている別のサイト(例えば銀行サイトやメールサービス)から、ユーザーの許可なくデータを読み取ったり操作したりすることを防ぐための非常に重要なセキュリティ機能です。例えば、悪意のあるサイト evil.com
が、ユーザーが bank.com
にログインしている間に、bank.com
のAPIに対してGETリクエストを送信し、ユーザーの口座情報を盗み出す、といったクロスサイトスクリプティング (XSS) やクロスサイトリクエストフォージェリ (CSRF) といった攻撃を防ぐ役割を果たします。
しかし、SOPは現代のWebアプリケーション開発において、意図的に異なるオリジン間でリソースを共有したい場合に問題となります。例えば、frontend.com
でホストされているフロントエンドが、api.com
でホストされているバックエンドAPIにアクセスしたい場合などです。このような正規のクロスオリジンリクエストを許可するためのメカニズムが、「Cross-Origin Resource Sharing (CORS)」です。
CORSは、サーバーがブラウザに対して「私のリソースは、これらの特定のオリジンからのリクエストに対して共有しても安全です」と明示的に伝えるための仕組みです。ブラウザは、サーバーからの応答に含まれる特定のHTTPヘッダーを確認し、リクエストを続行するかブロックするかを判断します。
FastAPIは、高速でモダンなPython Webフレームワークであり、API開発に最適です。FastAPIでは、uvicorn
などのASGIサーバー上で動作し、ミドルウェアを利用して様々なリクエスト処理を簡単に追加できます。CORSへの対応も、FastAPIの標準ミドルウェアとして提供されており、非常に簡単に設定できます。
本記事では、FastAPIにおけるCORS設定について、その基本的な仕組みから、詳細な設定方法、よくあるエラーとその対応策、そしてセキュリティ上の考慮事項まで、網羅的に解説します。
同一オリジンポリシー (SOP) とは?
CORSを理解するためには、まずSOPをしっかりと理解する必要があります。
「オリジン」とは、以下の3つの要素の組み合わせによって定義されます。
- スキーム (Scheme): プロトコル。例:
http
,https
- ホスト (Host): ドメイン名またはIPアドレス。例:
example.com
,localhost
,192.168.1.1
- ポート番号 (Port): サーバーのポート番号。例:
80
,443
,8080
,3000
これら3つの要素がすべて一致する場合にのみ、同一オリジンと見なされます。一つでも異なる場合、異なるオリジンと見なされます。
例:
– http://example.com/path/to/page
(スキーム: http, ホスト: example.com, ポート: 80 – httpのデフォルト)
– http://example.com:80/other/path
(スキーム: http, ホスト: example.com, ポート: 80)
– https://example.com/secure
(スキーム: https, ホスト: example.com, ポート: 443 – httpsのデフォルト)
– http://sub.example.com/
(スキーム: http, ホスト: sub.example.com, ポート: 80)
– http://example.com:8080/
(スキーム: http, ホスト: example.com, ポート: 8080)
– http://example.org/
(スキーム: http, ホスト: example.org, ポート: 80)
上のリストで、最初の二つ (http://example.com/path/to/page
と http://example.com:80/other/path
) は同一オリジンです。しかし、それ以外のURLは最初のURLとは異なるオリジンになります。スキームが違う (https
)、ホストが違う (sub.example.com
, example.org
)、ポートが違う (8080
) のいずれかに該当するためです。
ブラウザのSOPは、デフォルトでは、あるオリジンから取得したドキュメントやスクリプトが、異なるオリジンからリソースを読み取ること(HTTP GETリクエストなど)を制限します。特定のケース(例: CSS, images, scriptタグ、form submissionなど)ではクロスオリジンアクセスが許可されますが、これは主にリソースの埋め込みや送信に関するもので、応答の内容をスクリプトから読み取ることではありません。スクリプトによるクロスオリジンからの応答の読み取りは、SOPによってブロックされます。
CORS (Cross-Origin Resource Sharing) の仕組み
CORSは、サーバーが異なるオリジンからのスクリプトによるリソース読み取りリクエストを許可するための標準的なメカニズムです。これは、サーバーが特定のHTTPレスポンスヘッダーを返すことによって機能します。ブラウザはこれらのヘッダーを読み取り、リクエストを続行するか、またはSOP違反としてブロックするかを判断します。
CORSリクエストには主に2つのタイプがあります。
- 単純リクエスト (Simple Requests)
- プリフライトリクエスト (Preflighted Requests)
単純リクエスト (Simple Requests)
特定の条件を満たすリクエストは、「単純リクエスト」と見なされ、プリフライトリクエストなしに直接サーバーに送信されます。これらの条件は以下の通りです。
- メソッドが以下のいずれかであること:
GET
HEAD
POST
- ヘッダーが、ブラウザによって自動的に設定されるヘッダー(例:
User-Agent
,Accept-Language
,Content-Language
など)または、以下のヘッダーのみであること:Accept
Accept-Language
Content-Language
Content-Type
Range
Content-Type
ヘッダーの値が以下のいずれかであること:application/x-www-form-urlencoded
multipart/form-data
text/plain
ReadableStream
オブジェクトがリクエストに使用されていないこと。
単純リクエストの場合、ブラウザはリクエストを直接サーバーに送信し、リクエストヘッダーに Origin
ヘッダーを含めます。このヘッダーには、リクエストを送信しているページのオリジンが含まれます(例: Origin: http://frontend.com
).
サーバーがこのリクエストを受け取り、クロスオリジンからのアクセスを許可する場合、応答ヘッダーに Access-Control-Allow-Origin
ヘッダーを含めて返します。このヘッダーの値は、許可するオリジンを指定します。
“`http
HTTP/1.1 200 OK
Content-Type: application/json
Access-Control-Allow-Origin: http://frontend.com
Access-Control-Allow-Credentials: true # もしCredentialsが許可されている場合
Content-Length: …
{
“data”: “some data”
}
“`
ブラウザはサーバーからの応答を受け取ると、応答ヘッダーの Access-Control-Allow-Origin
の値を確認します。
– 値がリクエストの Origin
ヘッダーの値と一致する場合、ブラウザは応答をJavaScriptに公開します。
– 値が *
の場合(ただし Access-Control-Allow-Credentials
が true
でない場合)、ブラウザは応答をJavaScriptに公開します。
– それ以外の場合、ブラウザはSOP違反とみなし、応答をJavaScriptに公開せず、コンソールにCORSエラーを表示します。
プリフライトリクエスト (Preflighted Requests)
単純リクエストの条件を満たさないリクエスト(例: PUT
, DELETE
メソッド、またはカスタムヘッダーを含むリクエストなど)は、「プリフライトリクエスト」が必要になります。
プリフライトリクエストでは、実際のHTTPリクエストを送信する前に、ブラウザはサーバーに対して OPTIONS
メソッドを使用した「事前確認」リクエストを送信します。この OPTIONS
リクエストには、ブラウザが送信しようとしている実際のリクエストに関する情報(メソッド、ヘッダーなど)が含まれます。
OPTIONS
リクエストの例:
http
OPTIONS /api/resource HTTP/1.1
Host: api.com
Origin: http://frontend.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header, Content-Type
サーバーは OPTIONS
リクエストを受け取ると、そのオリジン (Origin
)、メソッド (Access-Control-Request-Method
)、およびヘッダー (Access-Control-Request-Headers
) に対して、実際のリクエストを許可するかどうかを判断し、その結果を応答ヘッダーでブラウザに伝えます。
サーバーの OPTIONS
応答ヘッダーの例:
“`http
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://frontend.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: X-Custom-Header, Content-Type, Authorization
Access-Control-Allow-Credentials: true # もしCredentialsが許可されている場合
Access-Control-Max-Age: 86400 # プリフライト結果をキャッシュする秒数
“`
ブラウザはサーバーからの OPTIONS
応答を受け取ると、以下のヘッダーを確認します。
Access-Control-Allow-Origin
: リクエストのOrigin
ヘッダーの値が許可されているか。Access-Control-Allow-Methods
: 実際のメソッド (PUT
) が許可されているか。Access-Control-Allow-Headers
: 実際のリクエストに含まれるすべてのカスタムヘッダー (X-Custom-Header
,Content-Type
) が許可されているか。Access-Control-Allow-Credentials
: 資格情報(クッキー、認証ヘッダーなど)を送信できるか。
これらのヘッダーの内容がブラウザが送信しようとしている実際のリクエストを許可する内容であれば、ブラウザは続いて実際のHTTPリクエスト(この例では PUT /api/resource
)を送信します。
もし許可されない内容であれば、ブラウザはSOP違反とみなし、実際のリクエストを送信せずにコンソールにCORSエラーを表示します。
プリフライトリクエストは、ブラウザがサーバーに余計な負荷をかけることなく、潜在的に問題のあるクロスオリジンリクエストを事前にチェックするための安全対策です。Access-Control-Max-Age
ヘッダーを使用することで、プリフライトの結果を一定期間キャッシュし、同じオリジンからの後続の同じ種類のリクエストに対してプリフライトを省略することができます。
FastAPI で CORS を設定する
FastAPIでは、fastapi.middleware.cors.CORSMiddleware
を使用することで、簡単にCORSポリシーを設定できます。これは ASGIミドルウェアとして実装されており、アプリケーションへのリクエストが実際のルートハンドラに到達する前にCORSチェックを行います。
CORSMiddleware
は、FastAPIアプリケーションオブジェクトに対して add_middleware
メソッドを使って追加します。
基本的な設定方法は以下の通りです。
“`python
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
許可するオリジン(フロントエンドのURLなど)
環境に応じて変更してください
origins = [
“http://localhost”,
“http://localhost:8000”, # FastAPIのデフォルトポート
“http://localhost:3000”, # React/Vue/Angularなどのデフォルトポート
“https://your-production-frontend.com”,
# 将来的に追加されるオリジンがあればここに追加
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=[““], # 全てのHTTPメソッドを許可する場合
allow_headers=[““], # 全てのHTTPヘッダーを許可する場合
)
@app.get(“/items/”)
async def read_items():
return [{“name”: “Foo”}, {“name”: “Bar”}]
@app.post(“/items/”)
async def create_item(item: dict):
return {“item”: item}
@app.put(“/items/{item_id}”)
async def update_item(item_id: int, item: dict):
return {“item_id”: item_id, “item”: item}
“`
このコードでは、CORSMiddleware
をアプリケーションに追加し、いくつかの重要なパラメータを設定しています。それぞれのパラメータについて詳しく見ていきましょう。
allow_origins
このパラメータは、どのオリジンからのリクエストを許可するかを指定します。最も重要かつ基本的なパラメータです。
-
リストで指定: 最も推奨される方法です。許可したいオリジンを文字列のリストとして指定します。
python
origins = [
"http://localhost:3000",
"https://my-frontend.com",
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
# ... other parameters
)
この設定では、http://localhost:3000
とhttps://my-frontend.com
からのリクエストのみが許可されます。それ以外のオリジンからのリクエストはブラウザによってブロックされます。 -
"*"
で全て許可:allow_origins=["*"]
と設定すると、全てのオリジンからのリクエストを許可します。
python
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
# ... other parameters
)
この設定は開発環境では便利ですが、本番環境での使用は慎重に行うべきです。特に、後述するallow_credentials=True
と組み合わせて使用することは絶対に避けるべきです。理由については後述します。APIがパブリックで、機密情報を含まないデータを返すだけであれば問題ない場合もありますが、セキュリティ上のリスクを十分に理解しておく必要があります。 -
正規表現で指定 (
allow_origin_regex
): 複雑なオリジンパターン(例: すべてのサブドメイン)を許可したい場合は、allow_origins
の代わりにallow_origin_regex
パラメータに正規表現パターンを指定できます。
“`python
import reexample.com およびそのすべてのサブドメインを許可
httpおよびhttpsの両方を許可
app.add_middleware(
CORSMiddleware,
allow_origin_regex=”https?://(.*\.)?example\.com”,
# … other parameters
)
``
allow_originsと
allow_origin_regex` は同時に指定できません。どちらか一方を使用してください。
allow_credentials
このパラメータは、クロスオリジンリクエストでクッキー、認証ヘッダー(例: Authorization
ヘッダー)、またはTLSクライアント証明書などの「資格情報 (Credentials)」を送信することを許可するかどうかを指定します。デフォルトは False
です。
allow_credentials=True
: 資格情報の送信を許可します。これは、セッション管理にクッキーを使用したり、トークンベースの認証(例: Bearerトークン)を使用する場合に必要になります。
python
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:3000"], # Specific origins MUST be listed
allow_credentials=True, # Allow cookies, auth headers, etc.
# ... other parameters
)
重要:allow_credentials=True
を設定する場合、allow_origins
に"*"
を指定することはできません。許可するオリジンを明示的なリストまたは正規表現 (allow_origin_regex
) で指定する必要があります。これはセキュリティ上の制約です。もしallow_origins=["*"]
とallow_credentials=True
を同時に設定しようとすると、ブラウザはCORSリクエストをブロックします。これは、任意のオリジンから資格情報付きでリクエストが送信できてしまうと、CSRF攻撃などのリスクが高まるためです。サーバーはAccess-Control-Allow-Origin: *
とAccess-Control-Allow-Credentials: true
の両方を返すことが標準で禁止されています。
allow_methods
このパラメータは、クロスオリジンリクエストで許可するHTTPメソッドを指定します。デフォルトは ['GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS']
です。
- リストで指定: 許可したいメソッドを文字列のリストとして指定します。
python
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_methods=["GET", "POST"], # Only GET and POST allowed
# ... other parameters
) "*"
で全て許可:allow_methods=["*"]
と設定すると、すべての標準的なHTTPメソッドを許可します。これはデフォルトのリストと同じ効果です。
python
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_methods=["*"], # Allow all default methods
# ... other parameters
)
通常、特に制限する必要がなければ["*"]
またはデフォルトを使用すれば十分です。
allow_headers
このパラメータは、クロスオリジンリクエストで許可するHTTPリクエストヘッダーを指定します。ブラウザは特定のヘッダー(例: Accept
, Content-Language
, Content-Type
など、前述の単純リクエストで許可されているもの)を常に含めますが、これ以外のカスタムヘッダー(例: X-Custom-Header
, Authorization
)を送信する場合、ここで明示的に許可する必要があります。デフォルトは空のリスト []
ですが、これはブラウザが自動的に許可するヘッダー以外は許可しないという意味になります。
- リストで指定: 許可したいヘッダー名を文字列のリストとして指定します。ヘッダー名は大文字・小文字を区別しませんが、標準的にはハイフン区切り小文字 (
content-type
) またはキャメルケース (ContentType
) が使われます。ミドルウェアは内部的に適切に処理します。
python
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_headers=["X-Custom-Header", "Authorization", "Content-Type"], # Allow these specific headers
# ... other parameters
) "*"
で全て許可:allow_headers=["*"]
と設定すると、ブラウザが送信する可能性のあるすべてのヘッダーを許可します。
python
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_headers=["*"], # Allow all headers
# ... other parameters
)
allow_headers=["*"]
は、開発環境では便利ですが、本番環境では必要なヘッダーのみを明示的にリストすることをお勧めします。これは、悪意のあるヘッダーを介した攻撃のリスクを減らすためです。特にAuthorization
ヘッダーをクロスオリジンで受け付ける場合は、allow_credentials=True
と共に、オリジン制限を厳格に適用する必要があります。
expose_headers
このパラメータは、ブラウザのJavaScriptコードからアクセスできるようになる、デフォルト以外の応答ヘッダーを指定します。デフォルトでは、JavaScriptは特定の安全な応答ヘッダー(例: Cache-Control
, Content-Language
, Content-Type
, Expires
, Last-Modified
, Pragma
)のみにアクセスできます。サーバーがこれらのヘッダー以外にカスタムヘッダーを応答に含め、それをフロントエンドのJavaScriptで読みたい場合、ここで指定する必要があります。デフォルトは空のリスト []
です。
- リストで指定: 公開したい応答ヘッダー名を文字列のリストとして指定します。
python
# 例:カスタムヘッダー 'X-Total-Count' をJavaScriptから読めるようにする
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
expose_headers=["X-Total-Count"],
# ... other parameters
)
max_age
このパラメータは、プリフライトリクエスト (OPTIONS
) の結果をブラウザがキャッシュできる最大秒数を指定します。デフォルトは 600
秒(10分)です。
- 秒数で指定: キャッシュしたい秒数を整数で指定します。
python
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
max_age=3600, # Cache preflight results for 1 hour
# ... other parameters
)
max_age
を長く設定すると、同じオリジンからの繰り返しリクエストに対するプリフライトリクエストの数を減らし、パフォーマンスを向上させることができます。しかし、CORS設定を変更した場合、ブラウザが古いキャッシュを使用している間は新しい設定が反映されない可能性があるため注意が必要です。
パラメータのまとめと推奨設定
パラメータ | 型 | デフォルト | 説明 | 推奨される設定 |
---|---|---|---|---|
allow_origins |
List[str] |
[] |
許可するオリジンのリスト。"*" で全て許可 (非推奨)。allow_origin_regex とは併用不可。 |
本番環境では、アプリケーションが実際にアクセスされる予定のオリジンを明示的にリストする。ローカル開発では "http://localhost:ポート番号" などを追加。 |
allow_origin_regex |
Optional[str] |
None |
許可するオリジンを定義する正規表現パターン。allow_origins とは併用不可。 |
複数のサブドメインなど、複雑なオリジンパターンを許可したい場合に検討。 |
allow_credentials |
bool |
False |
資格情報(クッキー、認証ヘッダーなど)の送信を許可するか。 | 認証にクッキーやAuthorization ヘッダーを使用する場合はTrue に設定。allow_origins が"*" の場合はTrue にできない。 |
allow_methods |
List[str] |
['GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'] |
許可するHTTPメソッドのリスト。"*" で全て許可。 |
通常はデフォルトまたは["*"] で十分。必要に応じて制限。 |
allow_headers |
List[str] |
[] |
許可するリクエストヘッダーのリスト。"*" で全て許可。 |
必要なカスタムヘッダー(例: Authorization , X-Requested-With など)を明示的にリスト。本番環境で"*" は非推奨。 |
expose_headers |
List[str] |
[] |
ブラウザのJavaScriptからアクセス可能な応答ヘッダーのリスト。 | JavaScriptから読みたいカスタム応答ヘッダーがある場合にリストする。 |
max_age |
int |
600 (10分) |
プリフライトリクエストの結果をキャッシュする秒数。 | 通常はデフォルトで十分だが、プリフライトが多い場合はパフォーマンスのために長くしても良い (例: 3600 = 1時間)。 |
ミドルウェアの順番
FastAPIでは複数のミドルウェアをチェインできます。CORSMiddleware
は通常、認証ミドルウェアなどよりも前に配置します。これは、CORSチェックは認証よりも低レベルの処理であり、不正なオリジンからのリクエストは認証レイヤーに到達する前にブロックされるべきだからです。add_middleware
に渡されるミドルウェアは、リストの最後に追加されたものから順に実行されます(内側から外側へ)。したがって、CORSMiddleware
を最初に add_middleware
で追加するか、リストの最後に指定することで、他のミドルウェアより外側(リクエスト処理の初期段階)で実行されるようにします。
“`python
app = FastAPI()
CORSMiddleware を最初に追加 (add_middleware呼び出し順の最初、実行順の外側)
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=[““],
allow_headers=[““],
)
例えば、その後に認証ミドルウェアを追加
from fastapi.middleware.authentication import AuthenticationMiddleware # 例
app.add_middleware(AuthenticationMiddleware, backend=SomeAuthBackend())
@app.get(“/protected/”)
async def read_protected(current_user: str = Depends(get_current_user)): # 認証が必要なルート
return {“message”: f”Hello {current_user}, this is protected data”}
@app.get(“/public/”)
async def read_public(): # 認証が不要なルート
return {“message”: “This is public data”}
``
/protected/` へのリクエストは、CORSミドルウェアによってブロックされ、認証ミドルウェアには到達しません。
この例では、CORSチェックが認証チェックよりも先に実行されます。不正なオリジンからの
CORS エラーとトラブルシューティング
CORSはWeb開発者にとってよくある落とし穴の一つです。正しく設定されていない場合、ブラウザのコンソールにエラーが表示され、APIへのリクエストが失敗します。ここでは、よくあるCORSエラーとそのトラブルシューティング方法について解説します。
CORS関連のエラーは、ブラウザのコンソールで確認するのが基本です。サーバー側のログには、通常、リクエストがミドルウェアでブロックされたという直接的なエラーメッセージは表示されません(リクエストがアプリケーションコードまで到達しないため)。
よくあるエラーメッセージ例 (ブラウザコンソール)
-
“Access to fetch at ‘…’ from origin ‘…’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.”
- 意味: クライアントのオリジンからのリクエストに対して、サーバーからの応答に
Access-Control-Allow-Origin
ヘッダーが含まれていませんでした。これは、サーバーがCORSを有効にしていないか、またはクライアントのオリジンを許可するように設定されていないことを意味します。 - 原因:
- FastAPIアプリケーションに
CORSMiddleware
が追加されていない。 allow_origins
(またはallow_origin_regex
) にクライアントのオリジンが含まれていない。- ミドルウェアの順番がおかしいなどで、
CORSMiddleware
が正しく機能していない。
- FastAPIアプリケーションに
- 対応策:
app.add_middleware(CORSMiddleware, ...)
がFastAPIアプリケーションの定義後に正しく記述されているか確認してください。allow_origins
リストに、リクエストを発行しているフロントエンドの正確なオリジン(スキーム、ホスト、ポートを含む)が含まれているか確認してください。例: フロントエンドがhttp://localhost:3000
で動いているなら、origins = ["http://localhost:3000"]
のようにリストに含めます。- 注意:
http://localhost
とhttp://localhost:8000
は異なるオリジンです。ポート番号も正確に指定する必要があります。http://example.com
とhttps://example.com
も異なるオリジンです。スキームも確認してください。 - もし正規表現を使っているなら、
allow_origin_regex
が正しくクライアントのオリジンにマッチするか確認してください。
- 意味: クライアントのオリジンからのリクエストに対して、サーバーからの応答に
-
“Access to fetch at ‘…’ from origin ‘…’ has been blocked by CORS policy: Response to preflight request doesn’t pass access control check: It does not have HTTP ok status.”
- 意味: プリフライトリクエスト (
OPTIONS
) が成功しませんでした。サーバーがOPTIONS
リクエストに対して 2xx 系のステータスコード(通常は 200 OK)を返しませんでした。 - 原因:
- サーバーサイドで
OPTIONS
メソッドが許可されていない、またはCORSMiddleware
がOPTIONS
リクエストを処理できていない。 - FastAPIアプリケーションのルーティングが、
OPTIONS
リクエストを処理しようとしてエラーになっている(例: 認証ミドルウェアなどがCORSミドルウェアより前にあり、OPTIONS
リクエストに対して認証を要求して失敗している)。 - APIエンドポイントが存在しないパスに対してリクエストしている(404 Not Foundが返ってくる)。
- サーバーサイドで
- 対応策:
allow_methods=["*"]
またはallow_methods=["OPTIONS", ...]
のようにOPTIONS
メソッドが許可されていることを確認します。CORSMiddleware
はデフォルトでOPTIONS
を許可しますが、明示的にリストを指定している場合は注意が必要です。CORSMiddleware
が認証ミドルウェアなどの他のミドルウェアより外側(前)で実行されるように、add_middleware
の呼び出し順を確認します。CORSMiddleware
は通常、他のミドルウェアが処理を開始する前にOPTIONS
リクエストに応答する必要があります。- ブラウザの開発者ツールで、実際に送られている
OPTIONS
リクエストとその応答を確認します。応答のステータスコードが200番台以外であれば、サーバー側で何かエラーが発生している可能性があります。
- 意味: プリフライトリクエスト (
-
“Access to fetch at ‘…’ from origin ‘…’ has been blocked by CORS policy: Request header field ‘X-Custom-Header’ is not allowed by Access-Control-Allow-Headers in preflight response.”
- 意味: プリフライトリクエスト (
OPTIONS
) の応答で、クライアントが送信しようとしているカスタムヘッダー(この例ではX-Custom-Header
)がAccess-Control-Allow-Headers
ヘッダーによって許可されていません。 - 原因:
allow_headers
に、クライアントが送信しているカスタムヘッダー名が含まれていない。
- 対応策:
- クライアントが送信しているすべてのカスタムヘッダー名を特定し、
allow_headers
リストに含めます。例:allow_headers=["X-Custom-Header", "Authorization", "Content-Type"]
- 開発中は手っ取り早く
allow_headers=["*"]
としても良いですが、本番環境では必要なヘッダーのみをリストすることを検討してください。
- クライアントが送信しているすべてのカスタムヘッダー名を特定し、
- 意味: プリフライトリクエスト (
-
“Access to fetch at ‘…’ from origin ‘…’ has been blocked by CORS policy: The value of the ‘Access-Control-Allow-Origin’ header in the response must not be the wildcard ‘*’ when the request’s credentials mode is ‘include’.”
- 意味: クライアントが資格情報(クッキーなど)を含めてリクエストを送信しているにも関わらず、サーバーからの応答の
Access-Control-Allow-Origin
ヘッダーが*
になっています。これはセキュリティ上の制約により許可されません。 - 原因:
- クライアント側で
credentials: 'include'
または同等の設定(例:withCredentials = true
)でリクエストを送信している。 - サーバー側で
allow_origins=["*"]
と設定している。
- クライアント側で
- 対応策:
- サーバー側で
allow_origins
を"*"
ではなく、具体的なオリジンリストまたは正規表現に変更します。例:allow_origins=["http://localhost:3000", "https://your-frontend.com"]
- または、もし資格情報が本当に不要であれば、クライアント側で資格情報を含めないように設定を変更します(
credentials: 'omit'
またはcredentials: 'same-origin'
)。ただし、通常API認証には資格情報が必要なので、サーバー側の設定変更が適切な場合が多いです。
- サーバー側で
- 意味: クライアントが資格情報(クッキーなど)を含めてリクエストを送信しているにも関わらず、サーバーからの応答の
-
その他のエラー: 特定のメソッドが許可されていない、特定の公開ヘッダーにアクセスできないなど、他のエラーメッセージが表示されることもあります。これらの場合も、メッセージの内容から原因(
allow_methods
,expose_headers
の設定不足など)を推測し、対応するパラメータを修正します。
トラブルシューティングの手順
CORSエラーに遭遇した場合、以下の手順で調査・解決を進めるのが効果的です。
- ブラウザコンソールを確認する: 最も重要なステップです。表示されているCORSエラーメッセージを正確に読み取ります。どのオリジンからのリクエストが、どのオリジンにあるリソースに対して、なぜブロックされたのか(
Access-Control-Allow-Origin
が無い/不正、プリフライト失敗、ヘッダー/メソッドが許可されていない、資格情報と*
の組み合わせなど)の情報が含まれています。 - ブラウザの開発者ツール(ネットワークタブ)を使用する:
- エラーが発生しているAPIリクエストを探します。
- そのリクエストをクリックし、詳細を確認します。
- 特に重要なのは「ヘッダー (Headers)」タブです。
- リクエストヘッダー (Request Headers):
Origin
ヘッダーが正しく送信されているか確認します。また、送信されているカスタムヘッダーやメソッド(特にプリフライトが必要なリクエスト)を確認します。 - 応答ヘッダー (Response Headers): サーバーからの応答に含まれる
Access-Control-Allow-Origin
,Access-Control-Allow-Methods
,Access-Control-Allow-Headers
,Access-Control-Allow-Credentials
,Access-Control-Expose-Headers
,Access-Control-Max-Age
などのヘッダーが期待通りになっているか確認します。
- リクエストヘッダー (Request Headers):
- プリフライトリクエスト (
OPTIONS
) を確認する: もしプリフライトが必要なリクエストの場合、実際のAPIリクエストの前にOPTIONS
リクエストが送信されているはずです。このOPTIONS
リクエストとその応答を特に詳しく調べます。OPTIONS
応答のステータスコードが200番台であること、そして応答ヘッダーが後続の実際のリクエスト(メソッド、ヘッダー、資格情報)を許可する内容になっていることを確認します。
- FastAPIサーバーのCORS設定コードを確認する:
CORSMiddleware
が正しくインポートされ、アプリケーションに追加されているか (app.add_middleware
)。allow_origins
: クライアントのオリジンがリストに含まれているか、または正規表現にマッチするか。ポート番号やスキーム(http/https)も含めて正確に確認します。末尾のスラッシュなども含めて完全に一致させる必要がある場合もありますが、通常オリジンはスキーム://ホスト[:ポート] の形式で指定します。allow_credentials
: クライアントが資格情報付きリクエストを送信している場合、これがTrue
になっているか。その場合、allow_origins
が"*"
ではないか。allow_methods
: 使用しているHTTPメソッドが許可されているか。allow_headers
: 使用しているカスタムヘッダーが許可されているか。expose_headers
: JavaScriptから読みたい応答ヘッダーが指定されているか。- ミドルウェアの順番を確認します。
CORSMiddleware
は通常、他の処理より前に来るようにします。
- FastAPIサーバーを再起動する: 設定変更を反映するには、サーバーの再起動が必要です。
- クライアント側の設定を確認する: JavaScriptの
fetch
やXMLHttpRequest
などでリクエストを送信する際、credentials: 'include'
やwithCredentials = true
などの設定が意図通りになっているか確認します。 - シンプルなケースで試す: 問題解決のために、一時的に
allow_origins=["*"]
,allow_methods=["*"]
,allow_headers=["*"]
と設定し、allow_credentials=False
にして試してみます。もしこの設定でCORSエラーが解消されるなら、問題は特定のオリジン、メソッド、ヘッダー、または資格情報の扱いにあります。そこから徐々に設定を絞り込んで、何が不足しているか特定します。ただし、この設定を本番環境に残さないように十分に注意してください。 - プロキシ設定を確認する: もしリバースプロキシ(Nginx, Apacheなど)を介してFastAPIアプリケーションを公開している場合、プロキシ側でCORS関連のヘッダーを誤って上書きしたり削除したりしていないか確認します。FastAPIのCORSミドルウェアが生成したヘッダーが、クライアントに正しく到達している必要があります。
セキュリティ上の考慮事項
CORSはクロスオリジンアクセスを可能にするためのものですが、設定を誤るとセキュリティリスクを招く可能性があります。特に以下の点に注意が必要です。
allow_origins=["*"]
の使用:- 危険性: 資格情報 (
allow_credentials=True
) と組み合わせて使用すると、インターネット上の任意のウェブサイトから、ユーザーのクッキーや認証情報を使用して認証済みリクエストをAPIに送信できてしまいます。これにより、CSRF攻撃などのリスクが著しく高まります。 - 例外: APIが完全にパブリックで、認証を必要とせず、機密情報を含まないデータのみを扱う場合は、セキュリティリスクは比較的低いと考えられます。しかし、それでも意図しない第三者からのアクセスを完全に排除したい場合は、やはりオリジンを制限すべきです。
- 推奨: 本番環境では、必ずアクセス元となるオリジンを具体的なリストで指定してください。ローカル開発環境でのみ、一時的に便宜上
"*"
を使用することを検討してください。
- 危険性: 資格情報 (
allow_methods=["*"]
およびallow_headers=["*"]
の使用:- 危険性: 必要以上に多くのメソッドやヘッダーを許可することは、攻撃ベクターを増やす可能性があります。例えば、許可すべきでないメソッド(例:
DELETE
)や、悪用される可能性のあるカスタムヘッダーを許可してしまうリスクがあります。 - 推奨: 実際にアプリケーションで使用するメソッドとヘッダーのみを許可リストに含めるのがベストプラクティスです。しかし、多くの場合は
allow_methods=["*"]
は問題ありません(標準的なHTTPメソッドのみが許可されるため)。allow_headers
については、Authorization
などの認証関連ヘッダーやカスタムヘッダーを明示的にリストすることをお勧めします。
- 危険性: 必要以上に多くのメソッドやヘッダーを許可することは、攻撃ベクターを増やす可能性があります。例えば、許可すべきでないメソッド(例:
allow_credentials=True
の使用:- 危険性: これを
allow_origins=["*"]
と組み合わせて使用した場合のリスクは前述の通り非常に高いです。また、allow_credentials=True
を使用すると、Access-Control-Allow-Origin
に*
が設定できなくなるという制約があるため、誤って設定するとブラウザがリクエストをブロックします。 - 推奨: 認証にクッキーや認証ヘッダーが必要な場合にのみ
True
に設定します。そして、必ずallow_origins
を具体的なオリジンリストで指定してください。
- 危険性: これを
セキュリティの原則として、「最小権限の原則」を適用することが重要です。つまり、必要最低限のオリジン、メソッド、ヘッダーのみを許可するようにCORSを設定します。
発展的な話題
動的なオリジン許可
許可するオリジンが環境や設定ファイルによって変わる場合、FastAPIアプリケーションの起動時にそれらを読み込んで origins
リストを構築するのが一般的な方法です。
“`python
import os
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
環境変数から許可オリジンを読み込む例
例: COMMA_SEPARATED_ORIGINS=”http://localhost:3000,https://prod-frontend.com”
allowed_origins_str = os.environ.get(“ALLOWED_ORIGINS”, “”)
origins = [origin.strip() for origin in allowed_origins_str.split(“,”) if origin.strip()]
デフォルトや開発用のオリジンを追加することも可能
if not origins:
origins = [
“http://localhost”,
“http://localhost:8000”,
“http://localhost:3000”,
]
print(f”Configured CORS origins: {origins}”)
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=[““],
allow_headers=[““],
)
@app.get(“/”)
async def read_root():
return {“Hello”: “World”}
“`
このようにすることで、デプロイ環境に合わせて許可オリジンを簡単に変更できます。
カスタムCORSロジック
CORSMiddleware
は一般的なCORS設定には十分ですが、例えばリクエストのヘッダーやパスに基づいて動的に許可するオリジンを決定したい、といったより複雑な要件がある場合は、独自のASGIミドルウェアを作成するか、FastAPIの依存性注入システムを使ってCORSヘッダーを自分で設定するなどの方法が考えられます。しかし、これはかなりの複雑さを伴うため、通常は CORSMiddleware
の設定で対応することをお勧めします。allow_origin_regex
で対応できる範囲であれば、それを利用するのが良いでしょう。
FastAPI Router と CORS
CORSMiddleware
は通常、FastAPIアプリケーションインスタンス(app = FastAPI()
)に対して一度だけ追加します。これにより、アプリケーション全体でCORSポリシーが適用されます。ルーター(APIRouter
)ごとに異なるCORS設定を適用することは、CORSMiddleware
の標準的な使い方ではありません。もし異なるCORSポリシーを適用したいAPIパス群がある場合は、アプリケーションを分割するか、カスタムミドルウェアでパスごとの処理を実装するなどの方法が必要になりますが、これは稀なケースです。多くの場合、アプリケーション全体で単一のCORSポリシーを適用することで十分です。
まとめ
CORSは、ブラウザのセキュリティ機能である同一オリジンポリシー (SOP) に基づく、クロスオリジンリソース共有のための重要なメカニズムです。サーバーは特定のHTTPレスポンスヘッダーを返すことで、どのオリジンからのリクエストを許可するかをブラウザに伝えます。
FastAPIは、fastapi.middleware.cors.CORSMiddleware
を提供することで、CORS設定を非常に簡単に行えます。allow_origins
, allow_credentials
, allow_methods
, allow_headers
などのパラメータを設定することで、柔軟かつセキュアなCORSポリシーを定義できます。
CORS関連のエラーは、主にブラウザのコンソールと開発者ツールのネットワークタブを使ってトラブルシューティングします。特にプリフライトリクエスト (OPTIONS
) の応答ヘッダーを確認することが重要です。
セキュリティ上の観点から、本番環境では allow_origins=["*"]
の使用を避け、資格情報 (allow_credentials=True
) を許可する場合は、許可オリジンを厳密にリストアップすることが極めて重要です。必要最低限のメソッドとヘッダーのみを許可するように設定することで、アプリケーションのセキュリティを向上させることができます。
FastAPIの強力なミドルウェアシステムを活用することで、CORSの複雑さを吸収し、安全で効率的なAPI開発に集中できます。本記事が、FastAPIにおけるCORSの理解と適切な設定に役立つことを願っています。