はじめに
React や Next.js から API を叩いたとき、
こんなエラーを見たことはありませんか?
Access to fetch at 'http://localhost:8000/api/users'
from origin 'http://localhost:3000' has been blocked by CORS policy
この瞬間、誰もがこう思います。
「え?通信できてるはずなのに、なぜかブロックされる…」
その正体が CORS(コルス) です。
この記事では、
・CORS とは何なのか
・なぜブラウザだけがブロックするのか
・なぜ Postman だと通るのか
・Laravel での正しい解決方法
・よくある “やらかしパターン”
まで、実務でできるだけ詰まらないレベルで解説 します。
今日のゴール
・CORS が「何をしている仕組み」なのかが説明できる
・なぜ React → Laravel でだけエラーが出るのか分かる
・Preflight Request(OPTIONS)の正体が分かる
・Laravel で CORS を正しく解除できる
まず結論:CORS は「ブラウザのセキュリティ機能」
CORS とは、
Cross-Origin Resource Sharing(オリジンをまたぐ通信の制御)
ブラウザが勝手にやっているセキュリティチェック
です。
つまり、
❌ サーバが通信を拒否している
✅ ブラウザが通信を止めている
これが最大の勘違いポイントです。
「オリジン」とは何か?
オリジンは次の3つの組み合わせで決まります。
① プロトコル(http / https)
② ドメイン(localhost / example.com)
③ ポート番号(3000 / 8000)
たとえば:
| URL | オリジン |
|---|---|
| http://localhost:3000 | A |
| http://localhost:8000 | B |
この2つは
同じ localhost でも「別オリジン」扱い になります。
なぜ CORS が必要なのか?
もし CORS がなかったらどうなるか?
あなたが悪意あるサイトにアクセスした瞬間、
・Twitter の投稿を勝手に削除
・銀行の送金APIを勝手に実行
・管理画面の設定を勝手に変更
これが ブラウザから自由に実行できてしまいます。
それを防ぐ最後の砦が CORS です。
通信の流れを超シンプルに図解
【React : localhost:3000】
↓
APIリクエスト
↓
【Laravel : localhost:8000】
↓
レスポンス
↓
【ブラウザ】
「この通信、許可されてる?」
↓
CORSヘッダーが無い → ❌ ブロック
ブロックしているのは “サーバ” ではなく “ブラウザ”
なぜ Postman や curl では CORS が出ないのか?
答えはシンプルです。
Postman や curl は “ブラウザではない” から
CORS は
✅ ブラウザ専用のセキュリティ機構
✅ サーバ間通信・CLI ツールには存在しません
だから
・Postman → 通る
・React → ブロックされる
という現象が起きます。
CORS を許可するために必要なヘッダー
サーバが以下のようなヘッダーを返すと、ブラウザは通信を許可します。
Access-Control-Allow-Origin: http://localhost:3000
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Authorization, Content-Type
つまり、
✅ 「このオリジンからの通信はOKですよ」
とサーバが宣言する必要があります。
Laravel で CORS を正しく設定する
Laravel では標準で
config/cors.php が用意されています。
設定例(開発環境)
return [
'paths' => ['api/*'],
'allowed_methods' => ['*'],
'allowed_origins' => ['http://localhost:3000'],
'allowed_headers' => ['*'],
'supports_credentials' => true,
];
これで、
✅ React(3000) → Laravel(8000)
✅ API 通信が正常に通る
ようになります。
Preflight Request(OPTIONS)とは?
PUT / DELETE / Authorization ヘッダー付き通信のとき、
ブラウザは いきなり本通信をしません。
代わりにまずこれを送ります。
OPTIONS /api/users
これを Preflight Request(事前確認) と言います。
ブラウザはここで、
✅ 「この通信、本当に送っていい?」
をサーバに確認してから、
OK → 本通信
NG → CORSエラー
という動きをします。
初心者がハマりやすい CORS 事故パターン
全オリジン許可を本番に入れる
'allowed_origins' => ['*'],
✅ 開発ではOK
❌ 本番では セキュリティ事故の原因
Authorization ヘッダーを許可していない
Access-Control-Allow-Headers: *
が無いと
✅ トークンを送った瞬間に CORS エラーになります。
Cookie 認証で supports_credentials を false にしている
'supports_credentials' => false
→ Cookie が送られず ログインできない地獄 に入ります。
今日のまとめ
・CORS は「ブラウザが勝手にやっているセキュリティ機構」
・サーバが拒否しているのではなく、ブラウザがブロックしている
・同一オリジンは「プロトコル・ドメイン・ポート」の3点セット
・Postman では CORS は発生しない
・Laravel では config/cors.php がすべて
・OPTIONS(Preflight)を理解すると CORS は怖くなくなる
次回 Day8 は…
「REST APIとは?RESTfulなURL設計を本気で考えてみる」
「それっぽいAPI」から
「ちゃんと設計されたAPI」にレベルアップする回です。