markdown
はじめに (対象読者・この記事でわかること)
この記事は、Webフロントエンド開発者やJavaScriptで外部APIを利用したいエンジニアを対象としています。同一生成元ポリシー(Same‑Origin Policy)の制約に悩んだことがある方へ、CORS(Cross‑Origin Resource Sharing)という仕組みが何であるか、ブラウザがどのようにリクエストを判定するか、サーバ側での設定方法までを体系的に理解できるよう解説します。実際にコード例を交えて設定手順を示すので、すぐに自分のプロジェクトに適用できるはずです。また、CORSエラーの原因診断やよくある落とし穴の回避策も併せて紹介します。この記事を読むだけで、CORSに関する基礎知識と実装の全体像が掴め、開発効率が向上します。
前提知識
この記事を読み進める上で、以下の知識があるとスムーズです。
- HTML・JavaScript の基本的な知識
- HTTP ステータスコードとヘッダーの概要
CORSポリシーの概要と必要性
Webブラウザは、セキュリティ上の理由から「同一生成元ポリシー」を適用し、スクリプトが自分のオリジン(プロトコル・ホスト・ポート)が一致しないリソースに直接アクセスすることを禁止します。この制限は、クロスサイトスクリプティング(XSS)や情報漏洩を防ぐための重要な防御線です。一方で、モダンなWebアプリはSNSのシェアボタンや外部分析サービス、クラウドAPIなど、複数のオリジン間でデータをやり取りするケースが増えています。そこで登場したのが「Cross‑Origin Resource Sharing(CORS)」です。CORSは、サーバが特定のオリジンからのリクエストを許可する旨をHTTPヘッダーで明示し、ブラウザはその指示に従ってリソースの受け渡しを許可します。つまり、サーバ側が「どのオリジンからのアクセスを許可するか」を宣言することで、同一生成元ポリシーを緩めつつも安全性を保てる仕組みです。
実装ステップ:CORSを正しく設定して安全に通信する
ステップ1 プリフライトリクエストの仕組みを理解する
GET/POST 以外のメソッドや、Content-Type が application/json のように非簡易リクエストヘッダーを伴う場合、ブラウザはまず OPTIONS メソッドで「プリフライト」リクエストを送ります。サーバは次のヘッダーを返す必要があります。
| ヘッダー | 目的 |
|---|---|
Access-Control-Allow-Origin |
許可するオリジン(* でも可) |
Access-Control-Allow-Methods |
許可する HTTP メソッド (GET,POST,PUT,DELETE など) |
Access-Control-Allow-Headers |
許可するリクエストヘッダー (Content-Type,Authorization など) |
Access-Control-Max-Age |
プリフライト結果のキャッシュ時間(秒) |
この段階でサーバが正しいヘッダーを返さないと、ブラウザは実際のリクエスト自体をブロックします。
ステップ2 サーバ側で CORS ヘッダーを付与する(Node.js/Express の例)
Js// server.js const express = require('express'); const app = express(); const corsOptions = { origin: ['https://example.com', 'http://localhost:3000'], // 許可したいオリジンの配列 methods: ['GET','POST','PUT','DELETE','OPTIONS'], allowedHeaders: ['Content-Type','Authorization'], credentials: true, // Cookie や Authorization ヘッダーを送信したい場合 maxAge: 86400 // 24時間キャッシュ }; app.use((req, res, next) => { const origin = req.headers.origin; if (corsOptions.origin.includes(origin)) { res.setHeader('Access-Control-Allow-Origin', origin); } res.setHeader('Access-Control-Allow-Methods', corsOptions.methods.join(',')); res.setHeader('Access-Control-Allow-Headers', corsOptions.allowedHeaders.join(',')); if (corsOptions.credentials) { res.setHeader('Access-Control-Allow-Credentials', 'true'); } if (req.method === 'OPTIONS') { res.setHeader('Access-Control-Max-Age', corsOptions.maxAge); return res.sendStatus(204); } next(); }); app.get('/api/data', (req, res) => { res.json({ message: 'CORS が正しく設定されています' }); }); app.listen(4000, () => console.log('Server running on http://localhost:4000'));
ポイント
1. 動的に Origin を設定:* を使うと credentials:true が無効になるため、許可したオリジンだけを列挙します。
2. OPTIONS ハンドリング:プリフライトリクエストに対しては 204 No Content を返すだけで OK。
3. Credentials:フロントエンドが Cookie や HTTP 認証情報を送る場合は Access-Control-Allow-Credentials:true が必須です。
ステップ3 フロントエンド側で fetch / XMLHttpRequest を呼び出す例
Js// client.js const url = 'http://localhost:4000/api/data'; fetch(url, { method: 'GET', credentials: 'include' // Cookie を送信したいとき }) .then(res => { if (!res.ok) throw new Error('Network response was not ok'); return res.json(); }) .then(data => console.log(data)) .catch(err => console.error('CORS エラーかその他の問題:', err));
credentials: 'include' を指定した場合、サーバ側の Access-Control-Allow-Credentials:true が必須になる点に注意してください。
ハマった点やエラー解決
| 症状 | 原因 | 解決策 |
|---|---|---|
No 'Access-Control-Allow-Origin' header is present on the requested resource. |
サーバが Access-Control-Allow-Origin を返していない。 |
オリジン検証ロジックを確認し、許可リストに対象オリジンが入っているか確認。 |
Response to preflight request doesn’t pass access control check: It does not have HTTP ok status. |
プリフライトで 200 系以外(例: 404, 500, 403)が返っている。 | OPTIONS 用のハンドラを正しく実装し、204 No Content を返すようにする。 |
Credential is not supported if the CORS header ‘Access-Control-Allow-Origin’ is ‘*’ |
Access-Control-Allow-Origin:* と credentials:true が同時に設定されている。 |
* をやめ、実際のリクエスト元を動的に設定する。 |
Request header field X-Requested-With is not allowed by Access‑Control‑Allow‑Headers |
フロント側が独自ヘッダーを送っているがサーバ側で許可していない。 | Access-Control-Allow-Headers に該当ヘッダー名を追加。 |
解決策のベストプラクティス
- オリジンはできるだけ絞る:
*を乱用するとセキュリティリスクが高まります。 - 環境変数で設定を管理:本番・ステージング・ローカルで許可リストが変わることが多いので、
.envに保存しコードから参照するようにするとミスが減ります。 - ログでプリフライト失敗を追跡:サーバ側で
OPTIONSが来たときのステータスコードとヘッダーをログに残すと、原因特定が容易です。
まとめ
本記事では、CORS の基本概念とブラウザがリクエストを制御する仕組み、Node.js/Express でのサーバ設定例、フロントエンドからの正しい呼び出し方、そして実装中に遭遇しやすいエラーとその対策を詳しく解説しました。
- CORS はサーバ側が許可オリジンを宣言することで同一生成元ポリシーを緩和する仕組み
- プリフライト (OPTIONS) の正しいハンドリングが成功の鍵
- Credentials を使う場合は Origin の動的指定とヘッダーの厳密な設定が必要
これらを実装すれば、外部 API との安全な通信が実現し、開発効率とユーザー体験が向上します。次回は、Cloudflare Workers や AWS API Gateway での CORS 設定についても取り上げる予定です。
参考資料
- MDN Web Docs – HTTP access control (CORS)
- Express – CORS middleware
- 「Webセキュリティ入門」 – 佐藤健, インプレス, 2022