「CORSエラー」を出しているのは、ブラウザ
開発中に、ブラウザの開発者ツールに「CORS」という文字の入ったエラーが出て、画面にデータが表示されない。サーバーは動いているし、同じ API を別の道具から呼ぶと普通に返ってくる。この食い違いの正体が CORS です。
CORSは、あるドメインのページが別のドメインとやり取りするとき、その結果をページに渡してよいかを、ブラウザがサーバーの返事で確かめる仕組みです。たとえば app.example.com のページが api.other.com へデータを取りに行くと、ブラウザはサーバーの返事に「このページからなら読んでよい」という印(レスポンスヘッダー)が付いているかを見ます。
マンションの管理人室にたとえると、よその建物から届いた返事を、住人に渡してよいかを差出人の許可を見て決める取り決めのようなものです。ここで大事なのは、ブラウザがいつも通信そのものを止めているわけではない、という点です。
- 普通のページ読み込みやフォーム送信に近い単純なリクエストは、実際にサーバーまで届き、返事も戻ってきます。許可の印がなければ、ブラウザがその返事をページのプログラム(JavaScript)に渡さないだけです。
- JSON を送る、独自の情報を付ける、データを消すといったリクエストでは、ブラウザが先に「このページから送ってもいいですか」と確認のリクエスト(プリフライト)を送ります。ここで許可が返ってこなければ、本番のリクエストは送られません。
つまり、画面上はエラーでも、単純なリクエストならサーバー側では処理が終わっていることがあります。「CORSで止まったから何も起きていない」とは限りません。
なぜ、ブラウザが確かめるのか
悪意のあるサイトが、訪問者のブラウザを勝手に使って別のサービスの API を呼び出し、返ってきたデータを読み取る。それを防ぐための、ブラウザの安全機能です。サーバーが「このドメインのページになら返事を見せてよい」と明示しておけば、正規のアプリだけが結果を受け取れます。
ブラウザの機能なので、サーバー同士の通信には関係しません。冒頭の「別の道具からなら返ってくる」のは、そのためです。「誰が」アクセスしているかを確かめる認証とも別物で、CORS が見ているのは「どのドメインのページから」です。
CORS の設定が必要になるのは、たとえば次のような場面です。
- 画面(フロントエンド)と API(バックエンド)を別のドメインで運用している
- 自社の API を、特定のパートナーのサイトからだけ使えるようにしたい
- 開発中に、手元の環境(localhost)から API を呼び出したい
*(全部許可)で良い場合、良くない場合
CORSエラーが出たら、とりあえず許可を * にしておけば消えるんでしょ?
許可するドメインを * にすると、どのサイトのページからでも返事を読めるようになります。誰に見られても困らない公開のデータや、ログインなしで使える公開 API なら、これは正当な設定です。
一方、ログイン状態(クッキーなど)を含む通信では、そもそも * は使えません。許可するドメインを具体的に指定する必要があり、さらに認証情報を含めてよいことを示す追加の設定も要ります。社内向けや会員向けのような非公開の API では、必要なドメインだけに絞ってください。
エラーを消したい一心で、ブラウザ側で「CORS を無効にする」方法を探すのは、問題の解決になりません。自分の画面で動いても、利用者のブラウザでは同じエラーが出ます。
ほかにも、つまずきやすい点があります。プリフライトが発生するリクエストは確認のぶん1往復多くなるので、応答に少し時間がかかります。開発環境と本番環境では許可するドメインが違うので、環境変数で切り替えられるようにしておくと混乱しません。