本当にあった、存在するはずのAPIにブラウザからアクセスできなかった話(Preflight requestとカスタムヘッダ編)

こんにちは。 エキサイト株式会社の三浦です。

今回は、私が実際に体験した、「存在するはずのAPIなのに、ブラウザからアクセスできなかった話」をしていきます。

作ったはずのAPIにアクセスできない…!?

私の携わっているサービスは長いこと続いているため、裏側にガタが来ているものも数多く存在しています。

少し前、そんな中の1つで、ブラウザから直にアクセスが来る、とあるAPIを新しいものにリビルドする、という仕事が舞い込んできました。

開発自体は順調でしたが、一つ問題がありました。 それは、「ある程度の期間は、新旧のAPIが共存しなければならない」ことです。

やり方は色々あります。

パスで分ける、クエリパラメータやボディの値で分ける、ドメインを変える…。

ただ、切り替えのためだけにわざわざそれらを作ることに少し違和感がありました。

そしてある日、一つの方法を思いついたのです。

「そうだ、ヘッダで分けよう!」

こちらで指定したカスタムヘッダが含まれている場合は新APIに、そうでない場合は旧APIにアクセスが行くようにするのです。

LB部分で分岐を考えるだけでよく、また切り替えのためにわざわざパスを変えたりドメインを作ったり、アプリケーションコードで分岐を考える必要もありません。

とても良い案だと思った私は、その方法で実装を進めていき、ついにテスト環境にリリースして試してみることになりました。

そこで、問題が起きるのです。

「あれ、アクセスが失敗する…!?」

アクセス失敗の原因

当然ですが、事前に直接APIを叩いたり、localhostの環境では試しており、それらの環境では問題なく動いていました。

ですが、テスト環境に上げた途端動かなくなるのです。

もちろんそのままリリースをすることは出来ません。

とりあえず開発者ツールを開いて色々と調べていくと、以下のエラーがネットワークで起きていました。

「CORS error」、すなわちブラウザ上で、あるサイトからドメインの異なるAPIにアクセスするときに起きるエラーです。

ただ、このエラーが起きないよう、API側でアクセス元サイトのURLは許可していたはずです。

更に調べると、今まで気にしたこともなかったあるリクエストが飛んでいることに気づきます。

preflight ってなんだ…?」

この preflight こそが、アクセスが出来ない原因だったのです。

Preflight requestとは

Preflight requestは、ブラウザが自動的に発行するリクエストで、CORSエラーが起きないかをチェックするためのものです。

developer.mozilla.org

プリフライトリクエストはブラウザーが自動的に発行するものであり、通常は、フロントエンドの開発者が自分でそのようなリクエストを作成する必要はありません。 これはリクエストが"to be preflighted"と修飾されている場合に現れ、単純リクエストの場合は省略されます。

JavaScriptなどでAPIへのアクセス処理があるとき、ブラウザはまずPreflight requestをAPIに対して送り、問題なければ実際のAPIへのリクエストを送る、という流れになっています。

(ただし、上記の引用の通り単純リクエストと呼ばれるリクエストの場合は、Preflight requestは省略されます。)

今回はカスタムヘッダを付けたために「単純リクエスト」の定義から外れ、Preflight requestが飛ぶようになっていました。

Preflight requestの問題点

さて、このPreflight requestですが、実はカスタムヘッダを付けることが出来ません。

それが今回の問題でした。

すなわち、「JavaScriptで書いたリクエスト」は新APIへアクセスされるのですが、「Preflight request」は旧APIへアクセスしに行ってしまい、結果としてエラーが生じていたのでした。

ヘッダでリクエストを振り分けること自体は良い発想でしたが、ブラウザからのアクセスの場合はこういった問題点があるようです。

結局このときは、パスによりリクエストを振り分けることにしたのでした。

最後に

Preflight requestは、普通に開発していると気になることはあまりないと思いますが、逆に詰まると少し解決に時間がかかってしまうかもしれません。

この記事がその解決の参考になれば幸いです。