フロントからAPIを叩くと「CORS policyによりブロックされました」という表示。
これはLaravelでAPI、Next.jsでフロントを実装した時に、よく遭遇するエラーです。
今回はCORSとはそもそもなにか、どうすればそのエラーを回避できるかについてまとめます。
CORSとは?
CORS(Cross-Origin Resource Sharing) は、「オリジン(= スキーム + ホスト名 + ポート)が異なる相手にブラウザがアクセスする際の許可ルール」です。ブラウザは必要に応じてプリフライト(OPTIONS
) リクエストを飛ばし、サーバからの許可ヘッダを確認してから本リクエストを送ります。
オリジン例
-
APIサーバー:
http://localhost:8000
-
フロント(Next.js):
http://localhost:3000
これらはオリジンが異なるためCORSが発動します。サーバーが適切なヘッダを返さないと、Chromeなどのブラウザが自動的にブロックします。
よくあるエラー
Access to XMLHttpRequest at 'http://localhost:8000/api/user' from origin 'http://localhost:3000' has been blocked by CORS policy:
Response to preflight request doesn't pass access control check
多くはLaravel側(APIサーバー側)のCORSやSanctumの適切な設定がされていないことで発生します。Cookieを扱う場合は Access-Control-Allow-Credentials: true
と Access-Control-Allow-Origin
の整合が必須で、*
は使えません。
CORSエラーを解決する手順(Sanctum/Cookie認証の完全版)
1. 追加パッケージは基本不要
Laravel 7以降はCORSサポートがフレームワークに組み込まれています(グローバルの HandleCors
ミドルウェアが対応)。追加で barryvdh/laravel-cors
を入れる必要は通常ありません。
※既存プロジェクトで config/cors.php
が見当たらない場合は、プロジェクトのバージョンやスケルトンを確認し、必要に応じて設定ファイルを用意してください
2. config/cors.php
の確認
Cookieベース認証(Sanctum)で最低限必要な例は次のとおりです。
<?php
return [
'paths' => ['api/*', 'sanctum/csrf-cookie'],
'allowed_methods' => ['*'],
'allowed_origins' => [
'http://localhost:3000', // フロントのオリジンを列挙(ポート違いは別オリジン)
'http://127.0.0.1:3000',
],
'allowed_headers' => ['*'],
'exposed_headers' => [],
'max_age' => 0,
'supports_credentials' => true, // Cookieを使うなら必須
];
注意点
-
supports_credentials
がtrue
のとき、allowed_origins
に*
は使えない - フロントが複数のオリジン(例:
localhost
と127.0.0.1
)の場合は、すべて列挙するか、URLを統一する
3. Sanctumの設定(CookieベースのSPA認証)
config/sanctum.php
の stateful
に、Cookieで認証させたいオリジン(ポート込み) を指定します。
<?php
'stateful' => [
'localhost:3000',
'127.0.0.1:3000',
],
.env
で管理する場合は次のようにします。
SANCTUM_STATEFUL_DOMAINS=localhost:3000,127.0.0.1:3000
SESSION_DOMAIN=localhost
補足
- ローカルでは
SESSION_DOMAIN=localhost
で十分なことが多い - サブドメイン間でCookieを共有する本番構成(
app.example.com
↔api.example.com
)では、SESSION_DOMAIN=.example.com
のように先頭ドット付きで指定
4. ミドルウェアの確認
SanctumのSPA認証を利用するためのミドルウェアは、Laravelのバージョンによって設定箇所が異なります。
-
Laravel 10以前:
EnsureFrontendRequestsAreStateful
が有効になっていることを確認 -
Laravel 11以降:
bootstrap/app.php
のミドルウェア設定で 「stateful API」相当を有効化(プロジェクトの雛形に従って設定)
api.php
のルート例(認証が必要なAPI)
<?php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
Route::middleware(['auth:sanctum'])->get('/user', function (Request $request) {
return $request->user();
});
5. Next.js側(Axios / fetch)の設定
Axiosを使う場合はCookie同送とCSRFトークン連携を有効にします。
import axios from 'axios';
export const api = axios.create({
baseURL: 'http://localhost:8000',
withCredentials: true, // Cookieを送る
xsrfCookieName: 'XSRF-TOKEN', // Laravel Sanctumのデフォルト
xsrfHeaderName: 'X-XSRF-TOKEN',
});
// 最初にCSRFを初期化してからログイン
await api.get('/sanctum/csrf-cookie');
await api.post('/login', { email, password });
// 以降は認証済みのままAPIを叩ける
const me = await api.get('/api/user');
fetch
を使う場合
await fetch('http://localhost:8000/sanctum/csrf-cookie', {
credentials: 'include',
});
await fetch('http://localhost:8000/login', {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify({ email, password }),
});
const res = await fetch('http://localhost:8000/api/user', {
credentials: 'include',
headers: { 'Accept': 'application/json' },
});
ポイント
-
Axiosは
withCredentials: true
、fetch
はcredentials: 'include'
を必ず付ける -
最初に
GET /sanctum/csrf-cookie
を叩いてCSRFトークンをセットしてから、/login
へPOSTする
本番環境での注意点
-
HTTPSは必須
Secure
属性付きCookieはHTTPSでしか送れません。ローカルで動いても、本番でHTTPのままだとCookieが送られず認証に失敗します。 -
ドメイン設計
SanсtumのSPA認証は同一トップレベルドメイン(サブドメイン可) を前提とします。app.example.com
↔api.example.com
はOK、example.com
↔example.net
のような別TLDはNGです(その場合はAPIトークン方式を使う)。 -
SameSiteとセキュリティ属性
別TLDに跨るCookieが必要な設計をどうしても採る場合はSameSite=None; Secure
が必要になりますが、Sanctumの想定外です。推奨構成は同一TLD + HTTPSです。 -
CORSを多層で設定しない
アプリ(Laravel)・Webサーバ(Nginx/Apache)・CDNでCORSをそれぞれ設定すると競合しがちです。責任レイヤーを1箇所に絞るのが安全です。 -
設定キャッシュ
設定を変更したらphp artisan optimize:clear
などでキャッシュをクリアしましょう。
それでも直らない場合のチェックリスト
チェックポイント | 確認内容 |
---|---|
config/cors.php |
supports_credentials=true か。allowed_origins に * を使っていないか。 |
CORSのパス |
paths に sanctum/csrf-cookie を含めているか。 |
フロントのオリジン列挙 |
localhost と 127.0.0.1 など、実際に使う全てのオリジン(ポート込み)を列挙したか。 |
Axios / fetch | Axiosは withCredentials と xsrfCookieName/xsrfHeaderName 、fetchは credentials:'include' を付けたか。 |
Sanctum |
stateful にフロントのオリジン(ポート込み)を入れたか。.env の SANCTUM_STATEFUL_DOMAINS は合っているか。 |
ミドルウェア | 使用中のLaravelのバージョンに応じて、Sanctumのstateful設定が有効になっているか。 |
HTTPS | 本番はHTTPSか。SESSION_SECURE_COOKIE=true (必要に応じて)になっているか。 |
プリフライト |
OPTIONS に対して200系で返しているか(Networkタブで確認)。 |
Cookie送信 | NetworkタブでCookieが付与されているか、Set-Cookie がブロックされていないか。 |
まとめ
-
CORSは「異なるオリジンに対するアクセスをブラウザが制御する仕組み」。サーバ(Laravel)が正しいヘッダを返す必要がある
-
SanctumのCookie認証では、CORS設定 + statefulドメイン + CSRF初期化 + フロントの設定がすべて揃って初めて動く
-
本番では同一トップレベルドメイン + HTTPSが基本。別TLDを跨ぐならCookieではなくAPIトークンを検討する
-
トラブル時はチェックリストに沿って、プリフライト、Cookie、設定キャッシュまで順に潰す
採用情報
アシストエンジニアリングでは一緒に働くフロントエンド、バックエンドエンジニアを募集しています!
少しでも興味のある方は、カジュアル面談からでもお気軽にお話ししましょう!
お問い合わせはこちらから↓
https://official.assisteng.co.jp/contact/