Contents
そもそもHTTP リクエストとは?
XMLHttpRequest

HTTP リクエストの手段は何がある?
XMLHttpRequestでは異なるドメインでアクセス、レスポンスの読み込みができません
プリフライトリクエストとは
- 本リクエストの前に送る確認用の
OPTIONSリクエスト。 - 「対象サーバーがそのクロスオリジンのリクエストを許可するかどうか」のCORSポリシーを事前に確認する目的で送られます
どのような場合に場合にプリフライトリクエストが送信されるか?
ステータスコード
HTTPのレスポンスヘッダの先頭に必ず入ってます
| ステータスコード | 名前 | 説明 |
|---|---|---|
| 200 | OK | リクエスト成功 |
| 202 | Accepted | リクエスト成功したが、処理はまだ |
| 301 | Moved Permanently | リソースは恒久的に移動 |
| 302 | Found | リクエストしたリソースは一時的に移動 |
| 307 | Temporary Redirect | リクエストしたリソースは一時的に移動 |
| 400 | Bad Request | リクエストが正しくない |
| 403 | Forbidden | アクセスが禁止されている |
| 404 | Not Found | 指定したリソースが見つからない |
| 405 | Method Not Allowed | 指定したメソッドを使えません |
| 406 | Not Acceptable | Accept関連のヘッダに問題 |
| 413 | Request Entity Too Large | リクエストボディが大きすぎる |
| 415 | Unsupported Media Type | サポートしていないメディアタイプ |
| 429 | Too Many Requests | リクエスト回数が多すぎる |
| 500 | Internal Server Error | サーバー側でエラーが発生 |
| 503 | Service Unavailable | サーバーが一時的に停止 |
より詳細は下記を参照
CORS と同一オリジンポリシー
Web ブラウザには同一オリジンポリシーがあり、異なるオリジン間のリソースアクセスは制限されます。CORS(Cross-Origin Resource Sharing)は、サーバーが特定の外部オリジンからのアクセスを許可する仕組みです。プリフライトは、この CORS 許可可否を事前確認するための仕組みです。
プリフライトの流れ
- ブラウザが
OPTIONSメソッドで以下のヘッダーを送信:
Origin: 要求元オリジン(例:https://app.example.com)Access-Control-Request-Method: これから送る本リクエストの HTTP メソッドAccess-Control-Request-Headers: 本リクエストで送る予定のカスタムヘッダー一覧
- サーバーは許可する場合、以下のヘッダーを含めて 200 などで応答:
Access-Control-Allow-Origin: 許可するオリジン(*も可。ただし認証絡みでは制約あり)Access-Control-Allow-Methods: 許可するメソッドAccess-Control-Allow-Headers: 許可するリクエストヘッダーAccess-Control-Max-Age: プリフライト結果のキャッシュ秒数- 必要に応じて
Access-Control-Allow-Credentials: true
- ブラウザは許可を確認できたら、本リクエスト(
GET/POST/PUT/...)を送信します。
ブラウザからの例(fetch)
await fetch('https://api.example.com/users', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer <token>',
},
body: JSON.stringify({ name: 'Taro' }),
credentials: 'include', // クッキーを伴う場合
});
上記は PUT と Authorization によりプリフライトが発生します。
サーバー側の設定例
Apache (httpd)
mod_headers を有効化のうえ、<VirtualHost> などで設定:
<IfModule mod_headers.c>
Header always set Access-Control-Allow-Origin "https://app.example.com"
Header always set Access-Control-Allow-Methods "GET,POST,PUT,DELETE,OPTIONS"
Header always set Access-Control-Allow-Headers "content-type,authorization"
Header always set Access-Control-Allow-Credentials "true"
Header always set Access-Control-Max-Age "86400"
# OPTIONS には即時応答(必要に応じて)
<If "%{REQUEST_METHOD} == 'OPTIONS'">
Header always set Content-Length "0"
</If>
</IfModule>
Nginx
location / {
if ($request_method = OPTIONS) {
add_header Access-Control-Allow-Origin "https://app.example.com" always;
add_header Access-Control-Allow-Methods "GET,POST,PUT,DELETE,OPTIONS" always;
add_header Access-Control-Allow-Headers "content-type,authorization" always;
add_header Access-Control-Allow-Credentials "true" always;
add_header Access-Control-Max-Age 86400 always;
return 204;
}
add_header Access-Control-Allow-Origin "https://app.example.com" always;
add_header Access-Control-Allow-Credentials "true" always;
}
Node.js (Express)
import express from 'express';
import cors from 'cors';
const app = express();
app.use(cors({
origin: 'https://app.example.com',
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
allowedHeaders: ['content-type', 'authorization'],
credentials: true,
maxAge: 86400,
}));
app.listen(3000);
PHP(フレームワーク未使用の素朴な例)
<?php
$origin = 'https://app.example.com';
header('Access-Control-Allow-Origin: ' . $origin);
header('Access-Control-Allow-Credentials: true');
header('Vary: Origin');
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
header('Access-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS');
header('Access-Control-Allow-Headers: content-type,authorization');
header('Access-Control-Max-Age: 86400');
http_response_code(204);
exit;
}
// 以降に本処理
トラブルシューティング
- ブラウザの開発者ツールのネットワークタブで
OPTIONS応答を確認 curlで再現:
curl -i -X OPTIONS \
-H "Origin: https://app.example.com" \
-H "Access-Control-Request-Method: PUT" \
-H "Access-Control-Request-Headers: content-type,authorization" \
https://api.example.com/users