はじめに
Web 開発をしていて引っかかり、きちんと理解していなかったので仕組みと解決方法を整理しました。
オリジンとは?
ブラウザが「同じオリジンかどうか」を判断する基準は次の 3 つです。
- スキーム (プロトコル) :
http
やhttps
- ホスト(ドメイン名 or IP) :
example.com
- ポート番号:
:80
,:443
,:3000
など
この 3 つが完全一致しないと「別オリジン」とみなされます。
例:
https://example.com:443
とhttps://example.com
→ OK (同じオリジン)https://example.com
とhttp://example.com
→ NG (プロトコルが違う)https://api.example.com
とhttps://example.com
→ NG (ホストが違う)
SOP
ブラウザには 同一オリジンポリシー (SOP: Same-Origin Policy)があり、
セキュリティのために異なるオリジンのリソースへ自由にアクセスできないよう制限されています。
しかし、フロントエンドと API サーバーが別オリジンだとアクセスできないのか?
その問題を解決するのがCORSです。
CORS を使えば、サーバー側が 「このオリジンからのアクセスは許可するよ」とブラウザに伝えることができ、フロントエンド側からのアクセスが可能になります。
CORS の仕組み
ブラウザが別オリジンのリソースを取得しようとすると、次の流れで動きます。
1. シンプルリクエスト
GET
や一部の POST
など、条件が緩いリクエストは直接送信されます。
サーバーがレスポンスに次のようなヘッダーを付ければ OKです。
Access-Control-Allow-Origin: https://frontend.com
ブラウザはこのヘッダーを見て、許可されていればレスポンスを JavaScript から利用できます。
2. プリフライトリクエスト (Preflight Request)
PUT
や DELETE
、カスタムヘッダー付きのリクエストなど「安全ではない」とされる場合、
ブラウザは本リクエストの前に OPTIONS
リクエストを自動で送ります。
# リクエスト
OPTIONS /api/data HTTP/1.1
Origin: https://frontend.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Content-Type
#サーバ側のレスポンス
Access-Control-Allow-Origin: https://frontend.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type
ブラウザはこれを見て OK なら、本リクエストを実行します。
代表的CORSの設定
以下がCORSに設定する代表的な項目になると思います。
- Access-Control-Allow-Origin : 許可するオリジン(* なら全許可)
- Access-Control-Allow-Methods : 許可する HTTP メソッド
- Access-Control-Allow-Headers : 許可するカスタムヘッダー
- Access-Control-Allow-Credentials : Cookie や認証情報を許可するか
著者は Go言語の echoフレームワークをよく使いますが、最近は以下のようなCORSの設定を行いました。
e := echo.New()
e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
AllowOrigins: []string{"http://localhost:3000", os.Getenv("FRONTEND_URL")},
AllowMethods: []string{"GET", "PUT", "POST", "DELETE"},
AllowHeaders: []string{
echo.HeaderOrigin,
echo.HeaderContentType,
echo.HeaderAccept,
echo.HeaderAuthorization
},
AllowCredentials: true,
}))
まとめ
- CORS は ブラウザ側のセキュリティ制約を緩和するために、サーバーが設定するルール
- フロントで「CORS エラー」と出ても、基本的には サーバーのレスポンス設定を直す必要がある
今回は以上となります。
ありがとうございました。
コメント