CORS とは — オリジン間リソース共有の仕組みと安全な設定
CORS(Cross-Origin Resource Sharing)は、ブラウザの同一オリジンポリシーを緩め、別オリジンからのリソース取得を明示的に許可する仕組みです。サーバーが Access-Control-Allow-Origin ヘッダーで「どのオリジンから応答を読んでよいか」を返します。書き方の基本、プリフライト、そして避けるべき危険な設定までを、正確に整理します。
CORS とは何ですか?
CORS はサーバーが Access-Control-Allow-Origin ヘッダーを返して、別オリジンからのリソース読み取りをブラウザに明示的に許可する仕組みです。ブラウザは既定で同一オリジンポリシーにより別オリジンへの fetch や XHR の応答読み取りを制限しますが、CORS はその制限を、サーバーが認めた範囲だけ安全に緩めます。
CORS は Cross-Origin Resource Sharing(オリジン間リソース共有)の略です。ここでいうオリジンとは、URL の「スキーム(https)・ホスト(example.com)・ポート」の 3 点セットのことで、 この 3 つが 1 つでも違えば「別オリジン」になります。ブラウザは安全のため、別オリジンのリソースを取りに行った応答の中身を、既定では JavaScript に読み取らせません。CORS は、その読み取りをサーバーの側から許可するための HTTP ヘッダーの取り決めです。
- 許可を出すのはサーバー — 応答に
Access-Control-Allow-Originを付けて「読んでよいオリジン」を宣言する - 判定するのはブラウザ — その宣言を見て、ページの JavaScript に応答を渡すか遮断するかを決める
- 対象は応答の読み取り — リクエスト自体はサーバーに届く。遮断されるのは「返ってきた中身を読むこと」
同一オリジンポリシーとどう関係しますか?
同一オリジンポリシーは、あるオリジンのページが別オリジンのリソースの応答を読み取ることを既定で禁止するブラウザの安全機構です。CORS はこれを完全に外すのではなく、サーバーが許可を返したオリジンに対してだけ読み取りを認める、上乗せの仕組みです。つまり CORS は同一オリジンポリシーに例外を作る手段だと言えます。
同一オリジンポリシーは、CORS の前提になっているブラウザの基本ルールです。これが無ければ、悪意あるサイトを開いただけで、 その JavaScript があなたのログイン中の別サービス(メールや銀行)に勝手にアクセスし、応答を読み取れてしまいます。 それを防ぐために、ブラウザは「別オリジンの応答は既定で読ませない」という壁を設けています。
CORS は、この壁を必要な相手にだけ、サーバーの許可のもとで開けるための仕組みです。壁を壊すのではなく、 サーバーが「このオリジンには読ませてよい」と明言したときだけ、ブラウザがその 1 枚だけ扉を開ける、というイメージが正確です。
Access-Control-Allow-Origin はどう書きますか?
許可したい特定のオリジン(https://example.com)か、全許可を意味する * のどちらかを指定します。ただし * は Cookie などの認証情報付きリクエストには使えません。認証情報を伴う場合はワイルドカードではなく、具体的なオリジンを 1 つだけ返す必要があります。原則として、必要なオリジンだけを許可します。
値の取り方は大きく 2 通り、https://example.com のような特定オリジンか、 全許可を表す *(ワイルドカード)です。多くの人が引っかかるのが、* は認証情報付きリクエストと併用できないという制約です。Cookie や Authorization ヘッダーを伴うリクエストで* を返しても、ブラウザは応答を読ませずにブロックします。
| 値 | 意味 | 認証情報付きリクエスト |
|---|---|---|
https://example.com | そのオリジンだけ許可 | 使える(推奨) |
* | 全オリジンを許可 | 使えない(ブラウザが拒否) |
| Origin を反射 | 実質すべて許可 | credentials と併用は危険 |
# 特定オリジンだけを許可する(推奨)
Access-Control-Allow-Origin: https://example.com
# 全オリジンを許可する。公開 API など、認証情報を伴わない場合のみ
Access-Control-Allow-Origin: *
# ★ NG: * と Cookie 等の認証情報は併用できない。
# 下の 2 行を同時に返すと、ブラウザは応答を読み取らせない
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true運用の原則はシンプルで、必要なオリジンだけを許可することです。* は「誰にも認証情報を渡さない完全公開の API」に限って使い、 それ以外は許可するオリジンを列挙します。関連するヘッダーとして、応答を許可するメソッドを示すAccess-Control-Allow-Methods、ヘッダーを示す Access-Control-Allow-Headers、 Cookie 等の送受信を許す Access-Control-Allow-Credentials があります。
プリフライト(OPTIONS)とは何ですか?
単純でないリクエストの前に、ブラウザが OPTIONS メソッドで「その操作を許可するか」をサーバーへ問い合わせる事前確認がプリフライトです。サーバーは Access-Control-Allow-Methods や Allow-Headers で許可するメソッド・ヘッダーを返し、ブラウザはその応答を見てから本番のリクエストを送るかどうかを決めます。
すべてのリクエストにプリフライトが付くわけではありません。GET や、フォーム送信相当の単純なリクエストはそのまま送られます。 一方で PUT・DELETE や、Content-Type: application/json・独自ヘッダーを伴うような「単純でない」リクエストは、副作用が起きる前に許可を確かめるため、ブラウザが自動でOPTIONS リクエストを先に送ります。これがプリフライトです。
# 1) ブラウザが先に送る事前確認(プリフライト)
OPTIONS /api/orders HTTP/1.1
Origin: https://app.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type
# 2) サーバーの応答。許可するメソッド・ヘッダーを明示する
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: Content-Type
Access-Control-Max-Age: 86400サーバーがプリフライトに正しく応答しないと、本番のリクエストは送られず、ブラウザのコンソールに CORS エラーが出ます。Access-Control-Max-Age を返すと、その秒数だけプリフライト結果をキャッシュでき、毎回の OPTIONS 往復を減らせます。
避けるべき危険な設定はありますか?
リクエストの Origin をそのまま反射(echo)しつつ Access-Control-Allow-Credentials: true を返す設定は危険です。これは事実上あらゆるオリジンへ認証付きアクセスを許すのと同じで、ログイン中の利用者の情報が悪意あるサイトから読み取られる恐れがあります。許可するオリジンは、あらかじめ決めた一覧だけに限定してください。
最も危険なのが、リクエストの Origin をそのまま反射(echo)しながら、認証情報の共有まで許してしまう設定です。Access-Control-Allow-Origin に送られてきた Origin をそのまま返し、加えてAccess-Control-Allow-Credentials: true を付けると、結果的にどのオリジンからでも Cookie 付きで応答を読める状態になります。 攻撃者のサイトを開いた利用者のログイン情報が、そのサイトの JavaScript に読み取られてしまいます。
# ★ 危険: 送られてきた Origin をそのまま返し、認証情報まで許可している。
# これは「どのサイトからでも Cookie 付きで読んでよい」に等しい
const origin = req.headers.origin;
res.setHeader("Access-Control-Allow-Origin", origin); // 反射(echo)
res.setHeader("Access-Control-Allow-Credentials", "true");
# ○ 安全: 許可する一覧を先に決め、含まれるときだけ返す
const ALLOWED = new Set(["https://app.example.com"]);
if (ALLOWED.has(origin)) {
res.setHeader("Access-Control-Allow-Origin", origin);
res.setHeader("Access-Control-Allow-Credentials", "true");
res.setHeader("Vary", "Origin");
}CORS はサーバーを守ってくれますか?
守りません。CORS はサーバーを守る仕組みではなく、相手のブラウザに課される制限です。したがって curl やサーバー間通信には効かず、悪意ある直接リクエストは防げません。それを防ぐのは認証・認可の役割で、CORS はあくまで正規ブラウザ上の JavaScript による横断的な読み取りを制御するものだと理解してください。
ここは CORS で最も誤解されやすい点です。CORS はサーバーを守るファイアウォールではありません。 チェックしているのは相手のブラウザであり、ブラウザを介さないアクセス —— curl、サーバー間の通信、プロキシ経由の呼び出し —— には 一切効きません。Access-Control-Allow-Origin をどう設定しようと、認証を通さない直接リクエストは普通に届いてしまいます。
- CORS がするのは — 正規ブラウザ上の JavaScript が、別オリジンの応答を読めるかを制御すること
- CORS がしないのは — サーバーへの直接アクセスを防ぐこと。これは認証・認可の仕事
設定はどう確認すればいいですか?
ブラウザの開発者ツールのネットワークタブで、対象リクエストの応答ヘッダーに Access-Control-Allow-Origin が期待どおりの値で付いているかを確認します。コンソールに CORS エラーが出ていれば、許可オリジン・メソッド・ヘッダーのいずれかが不足しています。curl でオリジンを指定して応答ヘッダーを直接見る方法も確実です。
確認の起点は、ブラウザの開発者ツールのネットワークタブです。対象リクエストを選び、応答ヘッダーにAccess-Control-Allow-Origin が期待した値で付いているかを見ます。プリフライトが絡む場合は、 本番リクエストの直前にある OPTIONS の応答も併せて確認します。コンソールの CORS エラー文には、 不足しているのがオリジンなのかメソッドなのかヘッダーなのかが書かれているので、まずそこを読みます。
コマンドラインで確かめるなら、Origin ヘッダーを付けてリクエストし、返ってくる応答ヘッダーを直接見るのが確実です。 セキュリティ関連のヘッダー全般については セキュリティヘッダーを、 コンテンツの読み込み元を制限する仕組みは Content-Security-Policy を併せて参照してください。 CORS(読み取りの許可)と CSP(読み込みの制限)は目的が逆向きの別物である点に注意します。