「ローカル環境でAPIを叩いたらCORSエラーになった」「フロントエンドとバックエンドのドメインが違うとエラーが出る」という経験は多くのフロントエンド開発者が持っています。CORSはセキュリティ上重要な仕組みですが、設定が複雑でエラーの原因がわかりにくいことも事実です。この記事では、CORSエラーの発生原因から、サーバー側の正しい設定方法、そして「とりあえず `*` にした」では解決しない問題まで、実務的な観点で解説します。
同一オリジンポリシーとCORSの関係
ブラウザには「同一オリジンポリシー(Same-Origin Policy)」というセキュリティ機能があり、スクリプトは自分と同じオリジン(スキーム+ホスト+ポートの組み合わせ)のリソースにしかアクセスできません。`https://app.example.com` のJavaScriptから `https://api.example.com` にfetchすると、ホストが異なるためブロックされます。CORS(Cross-Origin Resource Sharing)は、サーバーが「このオリジンからのアクセスは許可する」とブラウザに通知するための仕組みです。CORSはあくまでブラウザの制限であり、curlやPostmanなどのクライアントツールはCORSの影響を受けません。「curlでは動くのにブラウザで動かない」という状況はCORSが原因であることが多いです。
プリフライトリクエストとは何か
CORSの「プリフライトリクエスト」は、実際のリクエストを送る前にブラウザが自動的に送信するOPTIONSリクエストです。非シンプルリクエスト(`PUT`・`DELETE`・`PATCH` メソッド、カスタムヘッダー付き、Content-Typeが `application/json` のPOSTなど)はプリフライトが発生します。ブラウザは `OPTIONS /api/users` と `Access-Control-Request-Method: POST` などを含むリクエストを先に送り、サーバーが `Access-Control-Allow-Origin` と `Access-Control-Allow-Methods` を含むレスポンスを返せば本リクエストを送信します。REST APIを実装する際、OPTIONSメソッドへのハンドリングを忘れるとプリフライトが失敗してCORSエラーになります。これが「GETは動くのにPOSTで失敗する」という現象の典型的な原因です。
正しいAccess-Controlヘッダーの設定
`Access-Control-Allow-Origin` は許可するオリジンを指定します。`*`(ワイルドカード)は全オリジンを許可しますが、Cookieや認証ヘッダーを含むリクエスト(`credentials: "include"`)では `*` が使えず、具体的なオリジンを指定する必要があります。`Access-Control-Allow-Methods` は許可するHTTPメソッドをカンマ区切りで指定します。`Access-Control-Allow-Headers` はリクエストで使えるカスタムヘッダー(`Authorization`・`Content-Type` など)を指定します。`Access-Control-Max-Age` はプリフライトのキャッシュ時間(秒)で、設定することでプリフライトの発生頻度を減らせます。`Access-Control-Allow-Credentials: true` はCookieや認証情報の送信を許可します。この場合 `Allow-Origin` に `*` は指定できず、具体的なオリジンが必要です。
よくある誤設定と「CORSエラーを安易に回避する」危険性
`Access-Control-Allow-Origin: *` と `Access-Control-Allow-Credentials: true` を同時に設定しても無効で、ブラウザはエラーにします。これはCSRF(クロスサイトリクエストフォージェリ)への対策として意図的な設計です。開発環境で「とりあえずCORSを全許可」にしてそのまま本番に出てしまうことは、CSRF脆弱性につながります。正しい対応は「フロントエンドのオリジンのみを明示的に許可する」ことです。Next.jsなどのBFF(Backend for Frontend)を挟む構成では、フロントエンドとAPIが同一オリジンになるためCORS設定が不要になり、より安全です。HTTP Headers Referenceで各Access-Controlヘッダーの詳細仕様を確認できます。
まとめ
CORSは「セキュリティ上の制限」であり、バグではありません。正しい理解は「ブラウザが同一オリジンポリシーでアクセスをブロックしているのを、サーバー側のヘッダーで許可する」というものです。プリフライトリクエストへの対応・CredentialsとAllow-Originの組み合わせルール・本番での最小権限設定という3点を押さえることで、安全かつ正しいCORS設定が実装できます。