「JavaScriptでAPIを叩いたけど、なぜかCookieが送られない…」「サーバーでSet-Cookie
してるのに、ブラウザに保存されてない…」そんな経験ありませんか?
この問題、クロスオリジン通信(CORS)時の設定が原因であることが非常に多いです。特にcredentials: 'include'
やwithCredentials = true
の指定を忘れると、Cookieは送受信されません。
この記事では、上記の問題を解決する方法を解説しています。以下の内容をサンプルコードを用いてわかりやすく解説していますので、ご参考になれば幸いです。
credentials: 'include'
とは?withCredentials = true
とは?- Cookieが送受信されているかを確認する手順【fetch + Expressで検証】
credentials: 'include'とは?
JavaScriptのfetch()
でリクエストを送る際、デフォルトではCookieや認証情報は送信されません。クロスオリジン(異なるドメイン間)でリクエスト時にCookieを送信するためには、credentials: 'include'
を明示的に指定する必要があります。
fetch(url, {
credentials: 'include'
});
これを指定することで、同一オリジンでもクロスオリジンでもCookieを含めたリクエストが可能になります。
credentialsの種類一覧
値 | 説明 |
---|---|
'omit' | Cookieなどの認証情報は送信しない(デフォルト) |
'same-origin' | 同一オリジンのリクエストにはCookieを送信。クロスオリジンでは送信しない |
'include' | 常にCookieを送信する(クロスオリジン含む) |
サーバーでSet-Cookieした際の挙動
credentials: 'include'
を指定せずにクロスオリジンでAPIを叩くと、サーバーがSet-Cookie
を返しても、ブラウザはそのCookieを保存しません。表で表すと以下のようになります。
credentialsの値 | 同一オリジン | クロスオリジン |
---|---|---|
'omit' | ❌ 保存しない | ❌ 保存しない |
'same-origin' | ✅ 保存する | ❌ 保存しない |
'include' | ✅ 保存する | ✅ 保存する |
サーバー側にも設定が必要
クライアント側でcredentials: 'include'
を設定しても、サーバー側が適切にレスポンスヘッダーを返さないとCookieは送受信されません。サーバー側のレスポンスには、以下のようなヘッダーが必要です。
Access-Control-Allow-Origin: http://localhost:5500 ← リクエストを送るオリジンを指定
Access-Control-Allow-Credentials: true
なお、Access-Control-Allow-Origin: *
とAccess-Control-Allow-Credentials: true
は併用できません。Access-Control-Allow-Credentials: true
を設定した場合、オリジンはワイルドカード(*)ではなく、明示的に指定する必要があります。
withCredentials = trueとは?
withCredentials = true
は、fetch
以外のAPI(例:XMLHttpRequest
や axios
)で、Cookieを送信するために使うオプションです。これはfetch
のcredentials: 'include'
と同じ意味です。
// axiosの例
axios.get('http://localhost:4000/api/message', {
withCredentials: true
});
Cookieが送受信されているかを確認する手順【fetch + Expressで検証】
実際に、Cookieの送受信ができるかを確認してみましょう。手順を以下に示します。
ステップ
- APIサーバー(Express)を作成して起動する
- 別オリジンのフロントエンド(HTML + fetch)を作成する
- ブラウザでフロントHTMLを開く
- Cookieが送受信されているかを確認する
なお、今回使用するディレクトリ構成は以下の通りです。
credentialsSample/
├── frontend/ ← フロントエンド(HTML + fetch)
│ ├── index.html
│ └── script.js
└── apiServer/ ← APIサーバー側(Express)
└── server.js
APIサーバー(Express)を作成して起動する
まずAPIサーバーを作成します。サンプルコード(apiServer/server.js
)を以下に示します。
// 1. Expressモジュールを読み込んでインスタンス化してappに代入
const express = require('express');
const app = express();
// 2. CORS(クロスオリジンリクエスト)対策用のミドルウェアを設定
app.use((req, res, next) => {
// クライアント(http://localhost:5500)からのリクエストを許可
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:5500');
// 認証情報(Cookieなど)を含めることを許可
res.setHeader('Access-Control-Allow-Credentials', 'true');
// 許可するHTTPメソッドを指定
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
// 許可するHTTPヘッダーを指定
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
// プリフライトリクエスト(OPTIONSメソッド)には即時レスポンスを返す
if (req.method === 'OPTIONS') {
return res.sendStatus(204); // No Content
}
// 次のミドルウェアまたはルートに処理を渡す
next();
});
// 2. listen()メソッドを実行して4000番ポートで待機
const port = 4000;
app.listen(port, () => {
console.log(`サーバー起動中: http://localhost:${port}`);
});
// 3. 以下アプリケーションの処理を記述する
// "/api/message" にGETリクエストが来たときの処理
app.get('/api/message', (req, res) => {
// Set-Cookie ヘッダーを設定(名前: sample_cookie, 値: hello)
res.setHeader('Set-Cookie', 'sample_cookie=hello; HttpOnly; SameSite=Lax');
res.json({ message: 'こんにちは、クライアントさん!' });
});
作成したAPIサーバーを起動する前に、npmを使ってExpress
をインストールします。以下のコマンドを実行してください。
cd apiServer
npm init -y
npm install express --save
次にサーバーを起動します。カレントディレクトリがserver.js
が格納されているapiServer
ディレクトリであることを確認して、以下のコマンドを実行してください。
node server.js
これでサーバーが起動します。サーバーはhttp://localhost:4000
で待ち受けています。
別オリジンのフロントエンド(HTML + fetch)を作成する
frontend/index.html
のコードを以下に示します。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>Express API Test</title>
</head>
<body>
<h1>Express API テスト</h1>
<button id="fetchBtn">APIを呼び出す</button>
<p id="result">← ここに結果が表示されます</p>
<script src="script.js"></script>
</body>
</html>
frontend/script.js
のコードを以下に示します。
document.getElementById('fetchBtn').addEventListener('click', () => {
fetch('http://localhost:4000/api/message', {
method: 'GET',
credentials: 'include', // クッキーなどを送る axiosやXMLHttpRequestのwithCredentials = trueと同じ
})
.then((response) => {
if (!response.ok) {
throw new Error('APIエラー');
}
return response.json();
})
.then((data) => {
document.getElementById('result').textContent = data.message;
})
.catch((error) => {
console.error('エラー:', error);
document.getElementById('result').textContent = 'エラーが発生しました';
});
});
ブラウザでフロントHTMLを開く
クロスオリジン通信を確認するため、HTMLファイルをブラウザで直接開かず、Live ServerなどのローカルサーバーでHTMLファイルを開きます。VSCodeのLive Serverを使う場合、以下の手順で開くことができます。
index.html
が格納されているfrontend
ディレクトリでVSCodeを開く。index.html
を右クリックして「Open with Live Server」をクリックする。- ブラウザで
http://localhost:5500
を開く。
Cookieが送受信されているかを確認する
http://localhost:5500/index.html
にアクセスしたら、「APIを呼び出す」ボタンをクリックしてください。クリックすると、http://localhost:4000/api/message
にリクエストが送られます。ブラウザ画面に"こんにちは、クライアントさん!"
が表示されたらリクエスト成功です。
Google Chromeのデベロッパーツールを見ると、サーバーでSet-Cookie
されたCookieが保存されていることを確認できます。

また、2回目のリクエストでは、保存されたCookieをリクエスト時に送信していることも確認できます。

frontend/script.js
のcredentials: 'include'
を削除して、「APIを呼び出す」ボタンをクリックすると、サーバーでSet-Cookie
しているのにも関わらず、ブラウザにCookieが保存されていないことも確認できます。
本記事のまとめ
この記事では以下の内容を説明しました。
- クロスオリジンでCookieを送るには、
credentials: 'include'
(またはwithCredentials: true
)の指定が必須 - サーバー側では
Access-Control-Allow-Credentials: true
と、明示的なAccess-Control-Allow-Origin
の指定が必要 Access-Control-Allow-Origin: *
とAllow-Credentials: true
は併用できない- Cookieを保存・送信させるには、クライアントとサーバーの両方に正しい設定が必要
- 動作確認には、ローカルでも別オリジンになるよう
Live Server
などを使うと確実
お読み頂きありがとうございました。