こんにちは、AIシステムズです。
この記事は、代表コバが現場で対応してきたWebアプリ開発の知見をもとに、AIを活用して構成・執筆し、弊社にて最終チェックを行ったものです。
「ブラウザの開発者ツールに CORS policy: No 'Access-Control-Allow-Origin' header というエラーが出てAPIを呼べない」——フロントエンドとバックエンドを分けて開発している中小企業の現場で頻発するトラブルです。CORSエラーはバグではなく、ブラウザのセキュリティ仕様による正しい挙動であり、対応はサーバー側でレスポンスヘッダーを正しく返すことに尽きます。
- CORSエラーが起きる仕組み
- プリフライト(OPTIONSリクエスト)の役割
- サーバー側で設定すべきヘッダー
- nginx・Apache・PHP・Node.jsでの具体例
- セキュリティ上やってはいけない設定
目次
- CORSエラーが起きる仕組み
- プリフライトリクエストとは何か
- サーバー別の設定例
- Cookieを送る場合の追加対応
- こういうサイトに向いている/向いていない
- セキュリティ上の注意点
CORSエラーが起きる仕組み
CORS(Cross-Origin Resource Sharing)は、ブラウザが「異なるオリジン(スキーム+ホスト+ポート)」へのAPIアクセスを制限する仕組みです。同じドメインでもサブドメインが違う・ポートが違うだけで「別オリジン」扱いになります。
たとえば次の組み合わせは、すべて別オリジンです。
https://example.comとhttps://api.example.comhttp://localhost:3000とhttp://localhost:8080https://example.comとhttp://example.com(http/https違い)
別オリジンへのAPIアクセスは、サーバーが Access-Control-Allow-Origin ヘッダーで明示的に許可しないとブラウザがブロックします。
プリフライトリクエストとは何か
POST・PUT・DELETEや、独自ヘッダーを送るリクエストでは、ブラウザが本リクエストの前にOPTIONSメソッドでサーバーに「このリクエストを送ってよいか」を確認します。これがプリフライトです。
プリフライトに対してサーバーが正しいヘッダーを返さないと、本リクエストは送られません。CORS対応で最も漏れやすいのが「GETは通るのにPOSTで失敗する」というケースで、原因の多くはOPTIONSへの応答漏れです。
サーバー別の設定例
nginx
location /api/ {
if ($request_method = OPTIONS) {
add_header Access-Control-Allow-Origin "https://example.com";
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS";
add_header Access-Control-Allow-Headers "Content-Type, Authorization";
add_header Access-Control-Max-Age 86400;
return 204;
}
add_header Access-Control-Allow-Origin "https://example.com" always;
proxy_pass http://backend;
}
Apache(.htaccess)
Header set Access-Control-Allow-Origin "https://example.com"
Header set Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
Header set Access-Control-Allow-Headers "Content-Type, Authorization"
RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule ^(.*)$ $1 [R=204,L]
PHP(直接出力する場合)
header('Access-Control-Allow-Origin: https://example.com');
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type, Authorization');
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(204);
exit;
}
Node.js(Express)
const cors = require('cors');
app.use(cors({
origin: 'https://example.com',
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true,
}));
Cookieを送る場合の追加対応
認証付きAPIでCookieをクロスオリジン送信する場合は、サーバー側に Access-Control-Allow-Credentials: true を、クライアント側のfetchに credentials: 'include' を指定します。
このとき Access-Control-Allow-Origin: * は使えません。具体的なオリジンを明示的に書く必要があります。
// クライアント側
fetch('https://api.example.com/me', {
credentials: 'include',
});
こういうサイトに向いている/向いていない
CORS設定の整備は、フロントエンドとAPIを分離している中小企業の業務システムや、SaaS連携・外部公開APIを持つサイトに直接効きます。ECやお問い合わせフォームなど同一ドメイン内で完結している小規模サイトでは、そもそもCORSが発生しないため不要です。
セキュリティ上の注意点
Access-Control-Allow-Origin: *を本番でそのまま使うのは避ける。最低でも許可オリジンをホワイトリスト化する- Cookie認証のAPIでは、信頼できないオリジンを許可するとCSRFの足場になる
Access-Control-Allow-Headersに必要以上のヘッダーを書かない- 開発環境用の緩い設定が本番に残らないよう、環境ごとに設定を分離する
再発防止チェックリスト
- 許可オリジンをホワイトリスト化しているか
- OPTIONSメソッドに正しく応答しているか
- Cookieを使う場合、Allow-Credentialsとオリジン指定を整合させているか
- 開発・ステージング・本番で設定が分離されているか
- 必要な許可ヘッダー・メソッドだけに絞っているか
まとめ
CORSエラーは、フロントエンドのバグではなくサーバーがヘッダーを返していないことが原因です。「許可オリジンの明示」と「OPTIONSへの応答」を押さえれば、ほとんどのケースは解消できます。「とりあえず *」で逃げると、後から認証付きAPIに拡張するときにセキュリティ事故を招きます。
本記事は、代表コバがWebアプリ開発の現場で対応してきた知見をもとに、AIを活用して構成・執筆し、弊社にて最終確認を行っています。フロントエンド・APIの分離設計、CORSを含むセキュリティ設定の見直しについて、具体的な状況をふまえた相談を承っています。費用感だけ知りたい方も、お気軽にご相談ください。