はじめに
フロントエンドとバックエンドを別のドメインで実装する際に必ずと言っていいほど必要となるCORSについて概念から実装方法までまとめた記事になります。
CORSとは
CORS(Cross-Origin Resource Sharing)は、追加のHTTPヘッダーを使用して、あるオリジンで実行中のWebアプリケーションに、異なるオリジンのリソースへのアクセスを許可するように伝えるブラウザの仕組みです。
CORSが必要な理由
ブラウザには同一オリジンポリシー(Same-Origin Policy)というセキュリティ機能が組み込まれており、これは、あるオリジンのスクリプトが異なるオリジンのリソースにアクセスすることを制限する仕組みです。
例えば、
https://frontend.example.com
で動作するWebアプリケーションが
https://api.example.com
のAPIにアクセスしようとする場合
これは異なるオリジン間の通信となり、デフォルトでは制限されます。
CORSの構成要素
ヘッダー名 | 説明 | 例 |
---|---|---|
Access-Control-Allow-Origin |
許可するオリジンを指定 |
http://localhost:3000 または *
|
Access-Control-Allow-Methods |
許可するHTTPメソッドを指定 | GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS |
Access-Control-Allow-Headers |
許可するHTTPヘッダーを指定 | Content-Type, Accept |
Access-Control-Allow-Credentials |
クレデンシャル(Cookie等)の送信を許可 | true |
Access-Control-Expose-Headers |
クライアントに公開するヘッダーを指定 | Set-Cookie |
Access-Control-Max-Age |
プリフライトリクエストの結果をキャッシュする時間(秒) | 100 |
一部のフレームワークやライブラリではAccess-Control-Allow-Origin
を*
に設定できないことがあります。
Goで実装
package main
import (
"log"
"net/http"
)
func corsMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// CORSヘッダーの設定
w.Header().Set("Access-Control-Allow-Origin", "http://localhost:3000")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
// プリフライトリクエストの処理
if r.Method == "OPTIONS" {
w.WriteHeader(http.StatusOK)
return
}
next.ServeHTTP(w, r)
})
}
func main() {
// APIハンドラーの定義
apiHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`{"message": "Hello, World!"}`))
})
// ミドルウェアの適用
handler := corsMiddleware(apiHandler)
// サーバーの起動
log.Printf("Starting server on :8080")
if err := http.ListenAndServe(":8080", handler); err != nil {
log.Fatal(err)
}
}
コード説明
1. corsMiddleware関数
- HTTPハンドラーをラップするミドルウェア関数
- CORSに必要なヘッダーを設定
- OPTIONSメソッドによるプリフライトリクエストを処理
2. main関数
- シンプルなJSONレスポンスを返すAPIハンドラーを定義
- corsMiddlewareを適用
- 8080ポートでサーバーを起動
テストしてみる
Postmanやcorlコマンドではブラウザを中継しないため、CORSがどう動いているのかを確認できません。
今回はReactを用いて動作のテストをします。
npx create-react-app cors-test
cors-test/src/App.js
を以下のコードに書き換え
import React, { useState } from 'react';
function App() {
const [response, setResponse] = useState(null);
const [error, setError] = useState(null);
const testCORS = async () => {
try {
const res = await fetch('http://localhost:8080', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
const data = await res.json();
setResponse(data);
setError(null);
} catch (err) {
setError(err.message);
setResponse(null);
}
};
return (
<div style={{ textAlign: 'center', padding: '20px' }}>
<h1>CORSテスト</h1>
<button onClick={testCORS}>CORS APIを呼び出す</button>
{response && <div><h2>レスポンス:</h2><pre>{JSON.stringify(response, null, 2)}</pre></div>}
{error && <div><h2>エラー:</h2><pre>{error}</pre></div>}
</div>
);
}
export default App;
npm start
CORSの設定が正しい時の動作確認
上で説明したgoのコードを実際に動かしてみましょう
main.goに上記のコードを貼り付けて以下のコマンドを実行します
go run main.go
正常に動作するのを確認できたら
フロントのボタン(CORS APIを呼び出す
)をクリックしてみましょう
正しく動作している場合は以下のようにレスポンスが帰ってきます。
CORSの設定が誤っている時の動作確認
CORSの許可しているオリジンを変更してみます。
func corsMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 許可するオリジンをlocalhost:3001に変更
w.Header().Set("Access-Control-Allow-Origin", "http://localhost:3001")
// 省略
})
}
以下のようにエラーが表示されます。
ブラウザのコンソールを見るとオリジンが許可されているものが違うというエラーが見て取れますね。フロントエンドの開発をしている際、このようなエラーが出ていればCORSを疑ってみてください。
参考