背景
最近、私は個人ブログサイトを開発しています。小規模なプログラムなので、バックエンドには Node.js と Express フレームワークを使用し、フロントエンドには Vue 3、データベースには SQLite を選びました。
まずはデータベースとバックエンドプログラムを書き終え、PostMan で API のテストを行い、すべて正常に動作しました。
デバッグを簡単にするため、バックエンドに GET リクエストを受け取るテスト API を作成し、フロントエンドのセットアップ後にバックエンドと接続してテストしましたが、問題なく動作しました。
その後、ログイン API の接続を試みましたが、POST リクエストがバックエンドに届かないという問題が発生しました。
原因を調べてみると、どうやらバックエンドの CORS 設定に問題があったようです。設定を修正した後、無事解決しました。
さらに考えたこと
ところで、OPTIONS リクエストとは一体何なのでしょうか?もし設定を間違えた場合、なぜ GET テスト API は正常に動作するのでしょうか?
調べた結果得られた答え
1. OPTIONS リクエストとは?
OPTIONS リクエストは、HTTP プロトコルの一つで、サーバーがサポートする HTTP メソッドを取得するために使用されます。
- OPTIONS リクエストはブラウザの挙動であり、クロスオリジン(CORS)リクエストが行われる際、ブラウザは予め OPTIONS リクエストを送信してサーバーが対応可能なメソッドを確認します。
- 一般的に、AJAX リクエストにのみ制限がかかり、
link
、img
、script
、iframe
のようなリクエストには制限はありません。 - サーバー間でのクロスオリジンリクエストでは、OPTIONS リクエストは発生しません。
OPTIONS リクエストの特徴:
- リクエストボディはありません;
- レスポンスボディもありません;
- セキュア;
- 冪等性(同じリクエストを何度送っても結果が変わりません);
- キャッシュできません;
- フォーム内で使用できません。
2. シンプルリクエストと複雑リクエスト
CORS の予備チェックリクエスト(OPTIONS)を発生させないリクエストを「シンプルリクエスト」、予備チェックを発生させるリクエストを「複雑リクエスト」と呼びます。
シンプルリクエスト:
(次のすべての条件を満たす場合)
-
リクエストメソッド: GET、HEAD、POST。
-
許可されたヘッダーのみ使用:
Accept
、Accept-Language
、Content-Language
、Content-Type
など。 -
Content-Type
の値が次のいずれかの場合:application/x-www-form-urlencoded
、multipart/form-data
、text/plain
。※例えば、POST リクエストで
Content-Type: application/json
やトークン系のヘッダーを使用すると、OPTIONS リクエストが発生します。※フロントエンドで axios を使用して POST リクエストを送信する際、JavaScript オブジェクトを直接渡すと、axios は自動的にそれを JSON 文字列にシリアライズし、リクエストヘッダーの
Content-Type
をapplication/json;charset=UTF-8
に設定します。(これも私のプログラムで問題が発生した理由です)
複雑リクエスト:
(次のいずれかを満たす場合)
- リクエストメソッド:PUT、DELETE、CONNECT、OPTIONS、TRACE、PATCH。
- シンプルリクエスト以外のヘッダーを設定した場合。
-
Content-Type
の値が次のいずれかに該当しない場合:application/x-www-form-urlencoded
、multipart/form-data
、text/plain
。
3. 特定のリクエストヘッダー、レスポンスヘッダー
特定のリクエストヘッダー:
-
Access-Control-Request-Method
: 実際のリクエストで使用するメソッド(例:GET
、POST
)をサーバーに通知。 例:Access-Control-Request-Method: GET
-
Access-Control-Request-Headers
: 実際のリクエストで送信するカスタムリクエストヘッダーをサーバーに通知。 例:Access-Control-Request-Headers: token
特定のレスポンスヘッダー:
-
Access-Control-Allow-Methods
: クライアントが使用できるメソッド。 例:Access-Control-Allow-Methods: PUT
-
Access-Control-Allow-Origin
: 許可されたオリジン。すべてのオリジンを許可するには*
を設定。 例:Access-Control-Allow-Origin: http://localhost:8080
-
Access-Control-Allow-Headers
: 許可されたリクエストヘッダー。 例:Access-Control-Allow-Headers: token, Origin
-
Access-Control-Allow-Credentials
: クッキーを送信するかどうか。 例:Access-Control-Allow-Credentials: true
-
Access-Control-Max-Age
: 予備チェックリクエスト(OPTIONS)の結果をキャッシュする期間。 例:Access-Control-Max-Age: 60
(1分以内は再度 OPTIONS リクエストが発生しない)
以上です。お読みいただきありがとうございました。