「CORSエラー」は何が起きているのか
フロントエンド開発をしていると、ブラウザのコンソールにこのエラーが表示されることがあります。
Access to XMLHttpRequest at 'https://api.example.com/data'
from origin 'http://localhost:3000' has been blocked by CORS policy
このエラーは、ブラウザが**同一オリジンポリシー(Same-Origin Policy)**を守った結果です。まずはこの大元の仕組みを理解するところから始めます。
同一オリジンポリシー
ブラウザはセキュリティ上の理由から、異なるオリジンへのリクエストを制限しています。
オリジンとは「プロトコル + ドメイン + ポート番号」の組み合わせです。
| URL | 比較対象(http://example.com:80) | 同一オリジン? |
|---|---|---|
http://example.com/page | ポート省略(80と同じ) | ✅ 同一 |
https://example.com | プロトコルが違う | ❌ 異なる |
http://api.example.com | サブドメインが違う | ❌ 異なる |
http://example.com:8080 | ポートが違う | ❌ 異なる |
この制限がないと、悪意あるサイトが別サービスのAPIを呼び出して、ログイン中ユーザーのデータを盗めてしまいます。
CORS(Cross-Origin Resource Sharing)
CORS は「サーバーが明示的に許可したオリジンからのクロスオリジンリクエストを通す」仕組みです。禁止を緩和する仕組みではなく、サーバーの意図を表明する仕組みです。
シンプルリクエスト
GET や POST(Content-Type が application/x-www-form-urlencoded など)の場合、ブラウザはリクエストを直接送り、レスポンスの Access-Control-Allow-Origin ヘッダーを確認します。
# リクエスト
GET /data HTTP/1.1
Origin: http://localhost:3000
# レスポンス(サーバーが許可する場合)
Access-Control-Allow-Origin: http://localhost:3000
プリフライトリクエスト
PUT / DELETE / PATCH や Content-Type: application/json のリクエストでは、ブラウザが本番リクエストの前に**プリフライト(OPTIONSメソッド)**を送ります。
# プリフライト
OPTIONS /api/users HTTP/1.1
Origin: http://localhost:3000
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Content-Type
# サーバーの応答
Access-Control-Allow-Origin: http://localhost:3000
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type
Access-Control-Max-Age: 86400 # キャッシュ秒数
プリフライトが失敗(サーバーが許可しない)した場合、本番リクエストは送信されません。
CORSエラーの解決策
CORSエラーはサーバー側で設定を追加することで解決します。フロントエンドだけでは解決できません。
Express.js(Node.js)の例:
const cors = require('cors');
app.use(cors({
origin: ['http://localhost:3000', 'https://yourdomain.com'],
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true, // Cookie送信を許可する場合
}));
注意点: Access-Control-Allow-Origin: *(ワイルドカード)は credentials: true と組み合わせられません。Cookie や認証情報を送る場合は、オリジンを明示する必要があります。
重要なHTTPヘッダー
CORS以外にも、Web開発で頻繁に登場するHTTPヘッダーを押さえておきます。
リクエストヘッダー(クライアント→サーバー)
| ヘッダー | 役割 | 例 |
|---|---|---|
Authorization | 認証情報の送信 | Bearer eyJhbGci... |
Content-Type | リクエストボディの形式 | application/json |
Accept | 受け入れるレスポンス形式 | application/json |
Cookie | セッション情報など | session_id=abc123 |
レスポンスヘッダー(サーバー→クライアント)
| ヘッダー | 役割 |
|---|---|
Content-Type | レスポンスの形式(text/html, application/json など) |
Cache-Control | キャッシュの制御(no-cache, max-age=3600 など) |
Set-Cookie | クライアントにCookieを設定 |
Location | リダイレクト先URL(301/302と組み合わせ) |
セキュリティ関連のレスポンスヘッダー
| ヘッダー | 役割 |
|---|---|
Strict-Transport-Security | HTTPSを強制(HSTS) |
Content-Security-Policy | スクリプト・スタイルの読み込み元を制限(XSS対策) |
X-Content-Type-Options: nosniff | MIMEタイプのスニッフィングを禁止 |
X-Frame-Options: DENY | iframeへの埋め込みを禁止(クリックジャッキング対策) |
本番環境のWebアプリでは、これらのセキュリティヘッダーを適切に設定することが重要です。
まとめ
| 概念 | ひと言まとめ |
|---|---|
| 同一オリジンポリシー | ブラウザが異なるオリジンへのアクセスを制限する仕組み |
| CORS | サーバーが特定オリジンを許可することで制限を緩和する仕組み |
| プリフライト | 非シンプルリクエスト前にブラウザが送るOPTIONS確認 |
| CORSエラーの解決 | サーバー側で Access-Control-Allow-Origin を設定する |
CORSエラーに遭遇したとき、「ブラウザがサーバーの許可を確認している」と理解していれば、冷静に原因を特定できます。解決策はサーバーの設定変更です。
HTTPの基礎についてはHTTPとHTTPSの仕組みで学べます。