CORSとは
CORS(Cross-Origin Resource Sharing)は、異なるオリジン間でのリソース共有を安全に行うためのブラウザの仕組みです。セキュリティ上の理由から、ブラウザはデフォルトで異なるオリジンへのリクエストを制限しています。
オリジンとは: スキーム(http/https)、ホスト、ポートの組み合わせです。
https://example.com:443とhttps://api.example.com:443は異なるオリジンです。
同一オリジンポリシー
ブラウザは同一オリジンポリシー(SOP: Same-Origin Policy)により、異なるオリジンへのリクエストを制限します。
https://app.example.com から:
✓ https://app.example.com/api (同一オリジン)
✗ https://api.example.com/v1 (異なるホスト)
✗ http://app.example.com/api (異なるスキーム)
✗ https://app.example.com:8080 (異なるポート)
CORSの基本フロー
シンプルリクエスト
以下の条件を満たすリクエストはプリフライトなしで送信されます。
- メソッド: GET, HEAD, POST
- ヘッダー: Accept, Accept-Language, Content-Language, Content-Type(一部のみ)
- Content-Type: application/x-www-form-urlencoded, multipart/form-data, text/plain
ブラウザ → サーバー:
GET /api/users
Origin: https://app.example.com
サーバー → ブラウザ:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://app.example.com
プリフライトリクエスト
カスタムヘッダーやJSON送信など、条件を満たさないリクエストは事前確認が必要です。
1. プリフライト(事前確認)
ブラウザ → サーバー:
OPTIONS /api/users
Origin: https://app.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type, Authorization
サーバー → ブラウザ:
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, Authorization
Access-Control-Max-Age: 86400
2. 実際のリクエスト
ブラウザ → サーバー:
POST /api/users
Origin: https://app.example.com
Content-Type: application/json
Authorization: Bearer token123
CORSヘッダー
レスポンスヘッダー
| ヘッダー | 説明 |
|---|---|
| Access-Control-Allow-Origin | 許可するオリジン |
| Access-Control-Allow-Methods | 許可するHTTPメソッド |
| Access-Control-Allow-Headers | 許可するリクエストヘッダー |
| Access-Control-Allow-Credentials | Cookieの送信を許可 |
| Access-Control-Expose-Headers | JSからアクセス可能なヘッダー |
| Access-Control-Max-Age | プリフライト結果のキャッシュ時間 |
設定例(Express.js)
const cors = require('cors');
// 特定のオリジンを許可
app.use(cors({
origin: 'https://app.example.com',
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true,
maxAge: 86400
}));
// 複数のオリジンを許可
app.use(cors({
origin: ['https://app.example.com', 'https://admin.example.com'],
}));
// 動的にオリジンを検証
app.use(cors({
origin: (origin, callback) => {
const allowedOrigins = ['https://app.example.com'];
if (!origin || allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
}
}));
Credentialsモード
Cookieや認証情報を含むリクエストには特別な設定が必要です。
// クライアント側
fetch('https://api.example.com/data', {
credentials: 'include' // Cookieを含める
});
// サーバー側
// Access-Control-Allow-Origin: * は使用不可
// 具体的なオリジンを指定する必要がある
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Credentials: true
よくあるエラーと解決策
エラー1: No ‘Access-Control-Allow-Origin’ header
原因: サーバーがCORSヘッダーを返していない
解決策:
- サーバー側でCORSを設定
- プロキシを経由してリクエスト
エラー2: Wildcard with credentials
原因: credentials: 'include' と
Access-Control-Allow-Origin: * の組み合わせ
解決策:
- 具体的なオリジンを指定
Access-Control-Allow-Origin: https://app.example.com
エラー3: Method not allowed
原因: プリフライトでメソッドが許可されていない
解決策:
- Access-Control-Allow-Methods に必要なメソッドを追加
エラー4: Header not allowed
原因: カスタムヘッダーが許可されていない
解決策:
- Access-Control-Allow-Headers にヘッダーを追加
開発時のCORS対策
プロキシを使用
// Vite の設定例
export default defineConfig({
server: {
proxy: {
'/api': {
target: 'https://api.example.com',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
}
});
ブラウザ拡張機能
開発時のみCORSを無効化する拡張機能がありますが、本番環境では使用しないでください。
セキュリティ上の注意点
ワイルドカードの危険性
✗ 危険: すべてのオリジンを許可
Access-Control-Allow-Origin: *
✓ 推奨: 許可するオリジンを明示
Access-Control-Allow-Origin: https://app.example.com
オリジンの検証
// ✗ 悪い例: リクエストのOriginをそのまま返す
res.setHeader('Access-Control-Allow-Origin', req.headers.origin);
// ✓ 良い例: ホワイトリストで検証
const allowedOrigins = ['https://app.example.com'];
if (allowedOrigins.includes(req.headers.origin)) {
res.setHeader('Access-Control-Allow-Origin', req.headers.origin);
}
まとめ
CORSは、Webアプリケーションのセキュリティを維持しながら、異なるオリジン間でのリソース共有を可能にする重要な仕組みです。適切なヘッダー設定と、オリジンの検証を行うことで、安全なクロスオリジン通信を実現できます。
← 一覧に戻る