Cookie の仕組み - Set-Cookie / SameSite / Secure / HttpOnly

初級 | 15分 で読める | 2026.05.02

Cookieは保存値を後のリクエストで送る仕組みです。

ログイン状態などに使われ、条件が合うと送られます。

この記事では、サーバーの Set-Cookie から次のリクエストの Cookie まで、1つの往復を追います。

サーバーのSet-Cookieをブラウザが保存し、条件に合う次のリクエストでCookieを自動送信する往復を示す図

先に結論

Cookieの基本動作は次の3段階です。

  1. サーバーがレスポンスの Set-Cookie で保存を指示する
  2. ブラウザがCookieと属性を保存する
  3. 条件に合う次回リクエストで、ブラウザが Cookie を自動送信する

Set-Cookie はレスポンス、Cookie はリクエストのヘッダーです。

最初のレスポンスで保存する

利用者がログインし、サーバーがセッションIDを発行したとします。

HTTP/1.1 200 OK
Content-Type: application/json
Set-Cookie: sessionId=abc123; Path=/; Secure; HttpOnly; SameSite=Lax

{"message":"signed in"}

ブラウザは sessionId=abc123 だけでなく、後ろの属性も一緒に保存します。

部分意味
sessionId=abc123Cookieの名前と値
Path=/送信対象のパス
Secure原則HTTPSだけで送信
HttpOnlyJavaScriptからの読み取りを禁止
SameSite=Laxcross-site送信を制限

複数保存するときは、Set-Cookie をレスポンスに複数行含めます。

次のリクエストで自動送信する

保存条件に合うURLへアクセスすると、ブラウザは Cookie ヘッダーを付けます。

GET /account HTTP/1.1
Host: example.com
Cookie: sessionId=abc123

サーバーは値を受け取り、対応するセッションを探して利用者を識別できます。

フロントエンドのJavaScriptが毎回ヘッダーを組み立てるのではありません。Cookieはブラウザが管理し、条件に合えば自動で送信します。

複数のCookieが対象なら、1つのヘッダー内でセミコロン区切りになります。

Cookie: sessionId=abc123; theme=dark

送信されるかを決める条件

ブラウザは、主に次を確認します。

  • Domainまたは保存元ホスト
  • Path
  • Secure
  • SameSite
  • ExpiresまたはMax-Age

どれかの条件に合わなければ、そのリクエストには送りません。

Domain: どのホストへ送るか

Domain を省略したCookieは host-only Cookie です。設定したホストだけへ送られます。

たとえば app.example.com が次を返します。

Set-Cookie: sessionId=abc123; Path=/; Secure

このCookieは原則として app.example.com 向けで、api.example.com には送られません。

親ドメインを明示すると、条件に合うサブドメインでも使えます。

Set-Cookie: sessionId=abc123; Domain=example.com; Path=/; Secure

ただし、現在のホストと無関係なドメインは指定できません。example.com のサーバーが Domain=other.example を設定しても受け入れられません。

以前よく見た .example.com の先頭のドットは、現在の仕様では無視されます。

必要以上に広いDomainを付けない方が、別のサブドメインへ値が送られる範囲を狭められます。

Path: どのパスへ送るか

次のCookieは /account 以下のURLが対象です。

Set-Cookie: preference=compact; Path=/account

送信対象の例:

https://example.com/account
https://example.com/account/profile

対象外の例:

https://example.com/news

Path は送信範囲を整理する属性であり、セキュリティ境界ではありません。同じサイト内の別パスから絶対に見えなくする認可機能としては使えません。

Secure: HTTPSで送る

Set-Cookie: sessionId=abc123; Secure

Secure が付いたCookieは、原則としてHTTPSのリクエストだけに送られます。通信途中で値を盗み見られる危険を減らすため、認証Cookieでは必須と考えます。

Secure はCookieの内容そのものを暗号化する属性ではありません。ブラウザの保存領域では値がそのまま見えることがあるため、秘密情報を値へ直接入れないでください。

ローカル開発では localhost に特別な扱いがあるブラウザもありますが、本番条件はHTTPSで確認します。

HttpOnly: JavaScriptから読ませない

Set-Cookie: sessionId=abc123; HttpOnly

HttpOnly が付いたCookieは、ページ内JavaScriptの document.cookie から読み取れません。

XSSで悪意あるスクリプトが動いた場合に、セッションIDを直接盗まれる危険を下げます。ただし、そのスクリプトが利用者としてリクエストを送る危険まで消えるわけではありません。

また、HttpOnly はCSRF対策ではありません。Cookieの自動送信を利用した別の攻撃なので、SameSite やCSRFトークンなどを組み合わせます。

SameSite: cross-site送信を制限する

SameSite は、別サイトを起点とするリクエストでCookieを送るかを制御します。

Strict

Set-Cookie: sessionId=abc123; SameSite=Strict

cross-siteの文脈では最も厳しく送信を制限します。安全性は高い一方、外部サイトのリンクから戻った直後にログイン状態が反映されないなど、使い勝手へ影響することがあります。

Lax

Set-Cookie: sessionId=abc123; SameSite=Lax

cross-site送信を制限しつつ、一定のトップレベル遷移などでは送信を許すバランス型です。多くのブラウザは属性省略時にLax相当を使いますが、意図を明確にするため明示します。

None

Set-Cookie: widgetSession=xyz789; SameSite=None; Secure

cross-siteでも送信を許可します。SameSite=None には Secure が必要です。外部サイトへ埋め込む機能など、本当に必要な場合だけ選びます。

SameSiteの「site」は、単純な「ホスト名が完全一致するか」とは異なる定義です。まずは同一サイト内か、別サイト起点かを区別して理解しましょう。

有効期限: ExpiresとMax-Age

期限を指定しないCookieは、セッションCookieとして扱われます。

Set-Cookie: notice=closed; Path=/

ブラウザ終了で削除されるのが基本ですが、セッション復元機能によって残ることもあります。

秒数で寿命を指定するのが Max-Age です。

Set-Cookie: theme=dark; Max-Age=2592000; Path=/

日時で期限を指定するのが Expires です。

Set-Cookie: theme=dark; Expires=Mon, 24 Aug 2026 00:00:00 GMT; Path=/

両方がある場合は Max-Age が優先されます。認証セッションの有効期限は、Cookieだけでなくサーバー側でも検証します。

Cookieを削除する

ブラウザへ削除専用の命令を送るのではなく、同じ名前・Domain・PathのCookieを期限切れにします。

Set-Cookie: sessionId=; Max-Age=0; Path=/; Secure; HttpOnly; SameSite=Lax

設定時と削除時の PathDomain が違うと、元のCookieが残ることがあります。ログアウト後もCookieが見えるときは、属性の組み合わせを確認します。

Cookie名のprefix

対応ブラウザでは、__Secure- を付けるとHTTPSからの設定と Secure が、__Host- を付けるとさらに Domain なし・Path=/ が要求されます。

Set-Cookie: __Host-session=abc123; Path=/; Secure; HttpOnly

prefixは、不正な属性の組み合わせをブラウザに拒否させる補助です。

fetchでCookieを送る

同一originへのfetchでは、条件に合うCookieが通常送られます。

const response = await fetch("/api/me");

別originへCookieを含めるには、クライアント側でcredentialsを指定します。

const response = await fetch("https://api.example.com/me", {
  credentials: "include",
});

サーバー側でも、資格情報付きCORSを許可する設定が必要です。Access-Control-Allow-Origin: * と資格情報付き通信は組み合わせられません。

JavaScriptからレスポンスの Set-Cookie を直接読むことはできません。ブラウザが扱う禁止レスポンスヘッダーだからです。

curlで確認する

Cookie jarへ保存しながらレスポンスを取得します。

curl -i \
  -c cookies.txt \
  https://example.com/login

保存したCookieを次のリクエストで送ります。

curl -i \
  -b cookies.txt \
  https://example.com/account

DevToolsではApplicationタブのCookiesで保存値と属性を、NetworkタブのRequest Headersで実際に送られた Cookie を確認できます。

よくある勘違い

  • Set-Cookieを受け取れば必ず送信される: 各属性が判定され、保存が拒否される場合もあります。
  • HttpOnlyならCSRFも防げる: JavaScriptからの読み取り対策です。CSRFにはSameSiteなど別の対策が必要です。
  • Secureなら値も暗号化される: HTTPS通信に限定する属性で、値の暗号化ではありません。

Cookieへパスワードを保存してよい

Cookieはブラウザと通信に現れる値です。パスワードや重要な個人情報を直接保存しません。推測困難なセッション識別子を使い、サーバー側で状態と期限を管理します。

Pathで権限を分けられる

Pathは送信対象を決める仕組みです。アクセス権限はサーバーで検証します。

まとめ

Cookieは次の1本の流れで理解できます。

Set-Cookieを受信
→ ブラウザが値と属性を保存
→ URLと通信条件を照合
→ 合うリクエストへCookieを自動送信

主要属性の役割も分けて覚えましょう。

  • Domain / Path: 送信先の範囲
  • Secure: HTTPSに限定
  • HttpOnly: JavaScriptからの読み取りを禁止
  • SameSite: cross-site送信を制限
  • Max-Age / Expires: 有効期限

認証Cookieでは、一般に SecureHttpOnly、適切な SameSite を組み合わせ、サーバー側でもセッションを検証します。

次に読む

参考リソース

← 一覧に戻る
PR
PR
PR
PR