完璧なロジックでAPIを叩いたはずでした。
しかしChromeのコンソール画面を開いた瞬間、あなたは絶望します。
Access to fetch at ‘…’ from origin ‘…’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.
画面を埋め尽くす赤い文字。
「なんでやねん」
あなたは叫びます。
Postmanやcurlコマンドでは正常にデータが取れるのに、ブラウザからJavaScriptでリクエストした瞬間、鉄壁のガードに弾かれる。
多くの初心者がここで「Chrome CORS 無効化」などと検索し、セキュリティを低下させる拡張機能をブラウザに入れようとします。
待ってください。その行為は、家の鍵を捨てて泥棒を歓迎するのと同じです。
CORS(Cross-Origin Resource Sharing)は、あなたを苦しめるために存在するのではありません。
これはブラウザの過保護な愛なのです。
そもそも「オリジン」とは何か?
CORSを理解するには、まず「オリジン(Origin)」を知る必要があります。
Webの世界では、以下の3つの組み合わせを「オリジン」と呼びます。
- プロトコル(http, https)
- ドメイン(google.com, localhost)
- ポート番号(:80, :3000)
これらがすべて一致して初めて「同じ出身地(Same-Origin)」と見なされます。
一つでも違えば、それは「他所のオリジン」です。
- http://localhost:3000とhttp://localhost:8080 → 他人(ポートが違う)
- http://example.comとhttps://example.com → 他人(プロトコルが違う)
ブラウザは過保護なオカン
Webブラウザには大昔から同一生成元ポリシー(Same-Origin Policy: SOP)という鉄の掟があります。
これはあなたのブラウザが過保護なオカンだと考えてください。
「あんた!知らない人(他のオリジン)からアメ(データ)をもらっちゃダメよ!」
オカンは、あなたが site-a.comを見ているとき、裏で勝手にbank-b.comへリクエストを送って預金残高を盗み見られるのを防いでいるのです。
このSOPがあるおかげで、私たちは平和にネットサーフィンができます。
しかし現代のWeb開発では、フロントエンド(Reactなど)とバックエンド(API)のサーバーが別々であることが当たり前になりました。
つまり「知らない人からアメをもらう」ことが前提なのです。
そこで登場するのがCORSです。
これはオカン(ブラウザ)に「この人は友達だから大丈夫!」と納得させるための公式な手続きです。
仕組み:通行許可証を要求している
CORSエラーが出る最大の原因は、サーバーが通行許可を持っていないことに尽きます。
ここを勘違いしないでください。
あなたがクライアント側(JavaScript)でいくら叫んでも、オカンは納得しません。
相手(サーバ)が、身分を証明する必要があるのです。
ブラウザは他所のサーバにリクエストを送るとき、こっそりと以下のヘッダーを付けて送ります。
Origin:http://localhost:3000
「うちの子(localhost:3000)がそちらに行きますよ」という通知です。
これに対し、サーバはレスポンスで以下のように答える義務があります。
Access-Control-Allow-Origin:http://localhost:3000
「はい、localhost:3000君なら歓迎しますよ」
この Access-Control-Allow-Originこそが通行許可証です。
これがレスポンスに含まれていない、あるいは許可リストに自分のオリジンが入っていない場合、ブラウザは「許可がない!」と判断し、データを破棄してあの赤いエラーを出します。
謎の儀式「プリフライトリクエスト」
CORSにはもう一つ、開発者を混乱させる罠があります。
それがプリフライト(Preflight)です。
単純なGETリクエスト(シンプルリクエスト)なら、ブラウザはいきなり本番のリクエストを送ります。
しかし、以下のような「ちょっと凝ったこと」をしようとすると、ブラウザは警戒レベルを上げます。
- JSONデータを送る(Content-Type:application/json)
- PUTやDELETEメソッドを使う
- カスタムヘッダーをつける
この時、ブラウザは本番のリクエストを送る前に、自動的にOPTIONSメソッドという「確認の電話」をサーバーに入れます。
ブラウザ:「もしもし、これからJSONを持ったPUTリクエストを送りたいんですけど、受け付けてくれます?」
サーバ:「おう、ええよ。PUTもJSONも許可してるで」
ブラウザ:「よかった。じゃあ本番送ります」
この事前の確認が「プリフライトリクエスト」です。
もしサーバーがこのOPTIONSリクエストに対して適切な返事(200OK と許可ヘッダー)を返さないと、本番リクエストは送られることさえなくブロックされます。
解決策:サーバに「許可証」を書かせるべし
解決策はシンプルです。
クライアント側のコードをいじるのではなく、サーバー側の設定を変更し、レスポンスヘッダを追加します。
あなたがやるべきこと
- サーバー管理者に連絡する
APIが自社のものであれば、バックエンド担当者に「CORS許可ヘッダーをつけてくれ」と頼みましょう。 - プロキシサーバーを立てる
どうしてもサーバ設定を変えられない(他社のAPIなど)場合、自分のサーバを経由させてリクエストを送ります。サーバ間の通信にはCORS(オカン)は介入しません。 - 開発環境の設定を見直す
WebpackやViteなどの開発サーバには、プロキシ機能がついています。これを使えば、ブラウザを騙して「同じオリジン」だと思わせることができます。
禁断の果実「ワイルドカード」
面倒くさがりの開発者はこう考えます。
「許可オリジンを*にすれば全部解決じゃね?」
*(アスタリスク、ワイルドカード)は「誰でもウェルカム」という意味です。
パブリックなAPI(天気予報や為替レートなど)ならこれで構いません。
しかし、認証が必要なサービスや、ユーザの個人情報を扱うAPIでこれをやると、世界中の悪意あるサイトからもアクセス可能になります。
さらに、Access-Control-Allow-Credentials: true(Cookieなどを送る設定)を使う場合、* は使用できません。
仕様で明確に禁止されています。横着せずに許可するオリジンを明記しましょう。
CORSは敵ではない
CORSエラーが出て残業が確定したとき、モニターを殴りたくなる気持ちはわかります。
しかし、それはブラウザがあなたのユーザを守ろうと必死に働いている証拠です。
オカンの小言には耳を傾けましょう。
正しいヘッダをサーバに設定し、笑顔で「許可証」を提示してあげてください。
そうすれば赤いエラーは消え去り、お待ちかねのデータが手元に届くはずです。


コメント