AWSには慣れていますが、普段はこの領域の開発は行っていません。初歩中の初歩なのかもしれないけどCORSでハマりました。
こんなところでハマって時間を浪費するのはムダだなぁと思う(僕は3日くらい費やしてしまった……)
端的に解決手段を載せておきます。誰かのお役に立ちますように。
原理を理解しないとモヤモヤする方も多いと思います。自分が理解した範囲で関連事項をまとめておきます。真偽の裏付けは各自で取って下さい。
個人の備忘録です。手間を掛けたくないので、まずはテキストのみで公開します。ニーズが多いようならリライトして充実させていきます。
対象としている現象
以下の条件をANDで満たすものが該当します。
- API Gateway + LambdaでREST APIを構築しようとしている。
- API GatewayではHTTP APIを使用する
- 認証は用いない、オープンにアクセス出来るAPI
結論
セキュリティは後々、厳しくしていくとして、まずは動かしたいですよね。API Gatewayの設定を以下の通りにしてみてください。
- CORSの設定項目
リージョン:アクセス元のドメイン(今回はS3上の静的HTML内のJavaScriptから呼び出しました)
ヘッダー:*
メソッド:GETとかPOSTとか自分が使おうとしているもの+OPTIONS
- ルート設定
OPTIONSを追加すること
太字のものが見落としがちなところです。ヘッダーは関係するものだけに絞り込みたいところですが、思い切って全部許可しましょう。動くようになったあと、細かく指定して絞っていけばいいです。
OPTIONSの指定が難解なところです。意味不明だと思いますが、とりあえず設定してみてください。
技術解説
ここから先は興味のある方向けの記事です。原理や関連する情報についてまとめました。わかりやすさ優先で解説してます。
そもそもCORSの存在理由と原理
ザクッというとセキュリティ対策です。この記事を読んでいる人はきっとJavaScriptとかはわかっている前提で述べます。
JavaScriptを読み込ませ、人に感知させないようにサーバー間通信をさせることが可能ですよね。アクセスしているサイトと通信しているのなら通信の必要性は理解出来ます。しかし、その気になれば全然別のサーバーに通信させることも出来てしまいます。
通販で決済しようと思ってクレジットカードの情報を入力したら、全然別のサーバーにも送っていた。
こんな状況が発生すると笑えません。このセキュリティ対策として存在しているのがCORSという仕組みです。
ポイントとしておさえたいのは、このブロックの仕組みはブラウザ側に実装されている点です。サーバーサイドではありません。
基本的な原理としてはサーバー=REST APIにアクセスした時にどこにアクセスしてよいか?の許可情報がサーバーから配信されます。情報はヘッダー情報として配信されています。ここで許可されていなければ、ブラウザ側がCORS違反としてエラーを吐くという流れです。
API Gateway仕様 Lambdaの統合とは何か
API Gatewayにアクセスした際に起動されるLambda関数を指定すること。これを統合と表現しているようです。
ここでのポイントは統合をしたことによる影響です。実は、統合をすることによってヘッダーの応答はAPI Gatewayが行うようになります。Lambda内の応答コードは無視されるようになります。
他のソリューションでAPIを開発する場合、Lambdaに相当するプログラム上でアクセスした際に応答するヘッダーのコードを書くことがありますね。このノリでLambdaの中に書いたとしても期待する動きにはなりません。
このコード記述に相当するものがAPI GatewayにおけるCORS設定というわけです。
また、もう一つの重要なことはOPTIONSの設定が必要だということです。これは後述のプリフライト通信の中で使用されています。これを設定しないとプリフライト通信を完了出来ません。これはAPI Gatewayの仕様に起因するもののようです(あんまりちゃんと説明されてない上に内容が難しいから初学者にはよくわからん)
API Gateway仕様 プリフライト通信時の挙動
CORSの最初の通信でプリフライト通信(プリ=事前、フライト=飛行、本通信前の事前通信の意図でしょうね)を行います。このプリフライト通信が発生する条件は複数あるようです。今回、この記事を読んでいる方として理解すべきは、GET/POST以外の通信やContent-Typeがapplication/jsonの場合だということでしょうか。
要はプログラムっぽい処理の場合は必ず発生するよと思っておけば良さそうです。
プリフライト通信の過程でOPTIONSを使用します。この設定をし忘れることでCORSポリシー違反となります。ここが一番のハマりどころです。
ChatGPT先生にプリフライト通信の発生条件を提示してもらいました。参考にしてください。僕よりは詳しいです。
プリフライト通信(Preflight Request)は、特定の条件下で発生するCORS(Cross-Origin Resource Sharing)の一部です。これは、ブラウザがリクエストを送信する前に、安全性を確認するために行うHTTP OPTIONSリクエストです。プリフライト通信が発生する条件を以下にまとめます:
- HTTPメソッド:
GET
やPOST
などの単純なHTTPメソッド以外のメソッド(PUT
,DELETE
,CONNECT
,OPTIONS
,TRACE
,PATCH
)を使用する場合。- カスタムヘッダー: リクエストに標準外のヘッダーを含める場合(例えば、
X-PINGOTHER
、Content-Type
以外のもの)。- 特定の
Content-Type
:application/x-www-form-urlencoded
,multipart/form-data
,text/plain
以外のContent-Type
を使用する場合(例えば、application/json
)。- 読み取り専用リクエスト以外: CORSポリシーでは「単純なリクエスト」とみなされる、読み取り専用のリクエスト以外の場合。これには、特定の種類のクロスオリジンリクエストが含まれます。
HTTP APIとREST APIの違いは何か
実はCORSとは直接関係しませんが、関連設定の中で登場してくる言葉です。気になる人もいるかもしれないので少しだけ触れておきます。
結論から言うと、ローエンドがHTTP API、ハイエンドがREST APIです。
元々REST APIだけが存在していました。機能が豊富で細かいところまで設定出来ます。
しかし、現実の開発の中ではそこまでの機能性は求められず、それよりもパフォーマンスやコストが求められる事も多いです。このニーズに合わせて登場したのがHTTP APIです。機能を絞った代わりに、コストは3分の1程度となっていました。
この記事を読んでいるレベルの方の多くは、APIを作りたいなと思っている場合はHTTP APIの選択で十分なはずです。詳細な機能差については必要に応じて調べてみてください。
API Gateway + LambdaのCORSエラーでハマった際の解決方法について書いてみました。お役にたったなら、いいねでも押して行ってください。
最後まで読んでくださってありがとうざいました。