はじめに
CORS(Cross-Origin Resource Sharing) は、ブラウザが異なるオリジン間のリソースアクセスを制御するセキュリティ機構です。https://app.example.com から https://api.example.org へのリクエストは、デフォルトで 同一オリジンポリシー によりブロックされます。CORS は HTTP ヘッダーを通じてこの制限を安全に緩和します。本記事では CORS の全体像、プリフライトリクエストの仕組み、そしてエラーの解決策を解説します。
同一オリジンポリシー
オリジン は スキーム、ホスト、ポート の組み合わせで定義されます:
| URL | オリジン |
|---|---|
https://example.com/page1 | https://example.com |
https://example.com/page2 | 同一オリジン |
http://example.com/page1 | 異なるオリジン(スキーム違い) |
https://api.example.com | 異なるオリジン(ホスト違い) |
https://example.com:3000 | 異なるオリジン(ポート違い) |
同一オリジンポリシーは悪意あるサイトが他サイトのデータを読み取ることを防ぎます。
シンプルリクエストとプリフライトリクエスト
CORS は シンプルリクエスト と プリフライト を要するリクエストに分類されます。
シンプルリクエスト の条件:
- メソッド:
GET、HEAD、POST - Content-Type:
application/x-www-form-urlencoded、multipart/form-data、text/plain - カスタムヘッダーなし
シンプルリクエストは実際のリクエストを送信し、レスポンスの CORS ヘッダーを確認します。
それ以外のリクエストは プリフライト — 実際のリクエスト前に OPTIONS メソッドを送信します:
OPTIONS /api/data HTTP/1.1
Host: api.example.org
Origin: https://app.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type, Authorization
サーバーは適切な CORS ヘッダーで応答する必要があります。
CORS の主要レスポンスヘッダー
| ヘッダー | 役割 |
|---|---|
Access-Control-Allow-Origin | 許可するオリジンを指定 |
Access-Control-Allow-Methods | 許可する HTTP メソッドを指定 |
Access-Control-Allow-Headers | 許可するカスタムリクエストヘッダー |
Access-Control-Allow-Credentials | クレデンシャル(Cookie)送信を許可 |
Access-Control-Max-Age | プリフライト結果のキャッシュ時間 |
Access-Control-Expose-Headers | クライアントから参照可能なレスポンスヘッダー |
プリフライト応答の例:
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400
ワイルドカードと特定オリジン
Access-Control-Allow-Origin: * は任意のオリジンを許可しますが制約があります:
Access-Control-Allow-Credentials: trueと併用不可- 公開読み取り専用 API に限定すべき
クレデンシャル付きリクエストでは明示的なオリジン指定が必要です:
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Credentials: true
複数オリジンを許可する場合、サーバー側でリクエスト元のオリジンを検証し動的にエコーバックします。
Cookie 付き CORS の扱い
クロスオリジンで Cookie を送信するにはクライアントとサーバーの両方でオプトインが必要です:
クライアント(JavaScript):
fetch('https://api.example.org/data', {
credentials: 'include',
});
サーバー(レスポンスヘッダー):
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Credentials: true
credentials: 'include' がない限り、Cookie はクロスオリジンで送信されません。
よくある CORS エラーと解決策
| エラーメッセージ | 原因 | 解決策 |
|---|---|---|
No 'Access-Control-Allow-Origin' header present | CORS ヘッダー欠落 | Access-Control-Allow-Origin を追加 |
Origin mismatch | 許可リストにないオリジン | ホワイトリスト検証を実装 |
Credentials + wildcard | ワイルドカードとクレデンシャル | * でなく特定オリジンを指定 |
Method not allowed | メソッドが許可リストにない | Allow-Methods に追加 |
Header not allowed | カスタムヘッダー未許可 | Allow-Headers に追加 |
サーバー設定例
Express.js:
const cors = require('cors');
app.use(cors({
origin: 'https://app.example.com',
methods: ['GET', 'POST', 'PUT', 'DELETE'],
credentials: true,
}));
Nginx:
add_header Access-Control-Allow-Origin "https://app.example.com";
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
add_header Access-Control-Allow-Headers "Content-Type";
まとめ
CORS はユーザーを保護しつつ、正当なクロスオリジン通信を可能にする重要なセキュリティ層です。シンプルリクエストとプリフライトリクエストの違い、設定すべきヘッダー、クレデンシャルの適切な扱いを理解することで、CORS エラーに悩まされることなく安全なクロスオリジン通信を実現できます。サーバーサイドの CORS ポリシーを適切に構成し、セキュリティと利便性のバランスを取りましょう。
