PythonでCookieを扱うときは、jarに保持している値と実際に送る値、クライアントとサーバー、ブラウザの規則を分けて確認します。
| この記事の要点 |
|
Session を使うのが基本
Cookie は「1 回のリクエストの設定」ではなく「やり取りをまたいで持ち回る状態」です。requests.Session を使うと、サーバーから返ってきた Cookie を自動で保存し、次のリクエストでは domain・path・期限・Secure などの条件に合う Cookie だけを送ります。保持と送信は別です。
注意:旧概念例:example.com はログイン用の実サービスではなく、認証成功を確認したコードではありません。実パスワードへ置き換えて実行したり、get_dict()で認証値を出力したりしないでください。HTTP成功だけでは認証成功を意味しません。実行する例は「ローカルで送信範囲を確かめる」を使用してください。
import requests
session = requests.Session()
# 1. ログイン。返ってきたセッション Cookie が session に保存される
session.post("https://example.com/login",
data={"username": "taro", "password": "secret"},
timeout=10)
# 2. 以降のリクエストには自動で付く
res = session.get("https://example.com/mypage", timeout=10)
print(res.status_code)
print(session.cookies.get_dict()) # {'sessionid': 'abc123'}
session.close()
独立した requests.get() 呼び出し間では Cookie jar は引き継がれません。ただし同じ呼び出し内のリダイレクトでは Cookie が利用されることがあります。認証方式やサーバーによって応答は異なり、必ず401やログイン画面になるとは限りません。
Cookie を明示的に設定する
注意:旧概念例:domain省略のsetや辞書はホスト限定ではありません。「以降すべて」は誤りで、通常のjarは送信条件で選別されます。urlも未定義です。直接Cookieヘッダーはjarの選別を迂回するため使わず、下記の架空値・domain/path指定例を使ってください。
import requests
# その 1 回だけ付ける
res = requests.get("https://example.com/",
cookies={"lang": "ja", "theme": "dark"},
timeout=10)
# Session に常設する(以降すべてのリクエストに付く)
session = requests.Session()
session.cookies.set("lang", "ja")
session.cookies.set("sessionid", "abc123",
domain="example.com", path="/")
# ヘッダーとして直接書くこともできるが、Session の管理から外れるので非推奨
res = requests.get(url, headers={"Cookie": "lang=ja; theme=dark"}, timeout=10)
Requests の手動設定で domain を省略すると、現在ホスト限定にはなりません。session.cookies.set("lang", "ja") や辞書由来の Cookie は空 domain で作られ、異なるホストにも送られ得ます。サーバーの Set-Cookie で Domain を省略したブラウザの host-only Cookie と混同しないでください。手動設定は domain と path を明示し、不要な共有を避けます。Python の既定ポリシーはブラウザのスコープ規則を完全再現しないため、秘密値を扱う場合は送信先も制限してください。
受け取った Cookie を読む
注意:旧診断例:Cookieの値や生ヘッダーを本番ログへ出さないでください。get_dict()は同名で異なるdomain/pathを失い、get(name)はCookieConflictErrorになり得ます。domain/pathを指定するか、値を出さず属性を走査します。Set-CookieのExpiresにはカンマがあるため文字列をカンマ分割しません。
res = requests.get("https://example.com/", timeout=10)
print(res.cookies.get_dict()) # そのレスポンスで設定されたものだけ
print(res.cookies.get("sessionid")) # 無ければ None
print(res.headers.get("Set-Cookie")) # 生のヘッダー文字列
# 属性まで見たいときは 1 個ずつ走査する
for c in res.cookies:
print(c.name, c.value, c.domain, c.path, c.secure, c.expires)
| 書き方 | 返るもの |
|---|---|
res.cookies | そのレスポンスで新しく設定された Cookie |
session.cookies | これまでに現在 jar 内に保持されている Cookie(今回送る対象と同じとは限らない) |
res.request.headers.get("Cookie") | 最終リクエストで送信した Cookie ヘッダー。なければ None。値はログへ出さない |
「送ったつもりが送られていない」場合は 3 番目を確認します。ドメインやパスが合っていないと、保持されていても送信対象から外れます。
削除と更新
注意:旧操作例の訂正:上書きの単位は名前だけでなくdomainとpathの組です。del jar[name]は同名Cookieを複数スコープから消し得ます。clear()はローカルの削除だけで、サーバーのセッション失効やログアウトを保証しません。clear(domain, path, name)で対象を限定してください。
session.cookies.set("lang", "en") # 同じ名前なら上書き
del session.cookies["lang"] # 1 個削除
session.cookies.clear() # 全削除(ログアウト相当)
session.cookies.clear("example.com") # ドメイン単位で削除
print("lang" in session.cookies.get_dict()) # False
ログイン状態をファイルに保存する
注意:旧保存例は実行推奨ではありません。固定パスcookies.txt、存在確認後の読込み、ignore_discard=Trueで実セッションを平文保存する運用は避けてください。例はHTTPエラー確認・Sessionのclose・LoadError/OSError処理が不足しています。「架空値だけのLWP保存」で一時領域の限定検証を行います。
import http.cookiejar
import os
import requests
JAR_PATH = "cookies.txt"
jar = http.cookiejar.LWPCookieJar(JAR_PATH)
if os.path.exists(JAR_PATH):
jar.load(ignore_discard=True) # 前回の Cookie を復元
session = requests.Session()
session.cookies = jar # Session に差し込む
session.get("https://example.com/mypage", timeout=10)
jar.save(ignore_discard=True) # セッション Cookie も含めて保存
discard=True のセッション Cookie は save/load の既定では除外されます。これを含めるには両方で ignore_discard=True が必要です。ignore_expires=False が既定で、期限切れは別条件として除外されます。Python のセッションはブラウザの終了イベントではなく jar の管理方針です。ファイルは暗号化されず、漏えいすれば権限を悪用され得ます。実際の認証 Cookie は本例へ持ち込まず、共有・ログ出力・Git登録・平文エクスポートをしないでください。
サーバー側で Cookie を返す
Web アプリを自作する場合は Set-Cookie ヘッダーを組み立てます。標準ライブラリの http.cookies が名前・値や属性の文字列表現を組み立てますが、HTTPレスポンスへの追加、認証、署名・暗号化、適切なセキュリティ方針は自動では実装しません。複数 Cookie は別々の Set-Cookie ヘッダーとして返し、カンマで分割・結合しないでください。
注意:この例はヘッダー文字列の生成・解析だけです。sessionid=abc123は架空値であり、予測可能なIDを認証に使えません。printだけではクライアントへ返りません。HttpOnly/SameSiteはブラウザの規則で、Pythonの値の参照を禁止しません。Secureは送信方式を制限する属性で、保存ファイルの暗号化ではありません。
from http.cookies import SimpleCookie
cookie = SimpleCookie()
cookie["sessionid"] = "abc123"
cookie["sessionid"]["path"] = "/"
cookie["sessionid"]["max-age"] = 3600
cookie["sessionid"]["httponly"] = True # JavaScript から読めなくする
cookie["sessionid"]["secure"] = True # HTTPS のときだけ送らせる
cookie["sessionid"]["samesite"] = "Lax" # 別サイトからの送信を制限
print(cookie.output(header="Set-Cookie:"))
# Set-Cookie: sessionid=abc123; HttpOnly; Max-Age=3600; Path=/; SameSite=Lax; Secure
# 受け取ったヘッダーを解析する
incoming = SimpleCookie("lang=ja; theme=dark")
print(incoming["lang"].value) # ja
| 属性 | 意味 | 推奨 |
|---|---|---|
HttpOnly | JavaScript から読めなくする | ブラウザの非HTTP API(document.cookie等)からの読取りを制限。Pythonからの読取りやXSS全体を防ぐものではない |
Secure | HTTPS のときだけ送る | 本番では付ける |
SameSite | 他サイト由来のリクエストで送るか | ブラウザの cross-site 送信方針。Strict は外部からの遷移・決済復帰を妨げ得るため用途で選ぶ。Requests はブラウザの SameSite 制御を実装しない |
Max-Age / Expires | 有効期限 | 省略はセッション扱い。ブラウザのセッション復元で残る場合もある。Max-Age と Expires が両方あれば Max-Age が優先 |
Path / Domain | 送信対象の範囲 | ブラウザは Domain 省略なら host-only、指定すればサブドメインにも及ぶ。Path は送信条件でありアクセス制御ではない。Cookie はポートで分離されない |
よくある詰まりどころ
- Session を使っていない — 単発の
requests.get()では Cookie が引き継がれない - リダイレクト先で消える —
allow_redirects=Falseにして途中のSet-Cookieを確認する - ログインが JavaScript 経由 — フォーム POST ではなく API を叩いている。ブラウザの開発者ツールで実際のリクエストを見る
- CSRF トークンが必要 — ログインページを一度 GET してトークンを取り出し、POST に含める
- 日本語や記号が入る値 — 送受信側で合意した表現にする。quote は任意の値を自動復元する仕組みでも暗号化でもなく、SimpleCookie がURLデコードするわけではない
関連
- 文法 — 親カテゴリ
- httpリクエスト(curl)をする方法
- Responseオブジェクトの中身の確認
- Webスクレイピング
- cookieの値の設定と取得 — Django での扱い
ローカルで送信範囲を確かめる
以下は127.0.0.1の一時HTTPサーバーだけに接続する専用例です。実サイト・資格情報・外部ネットワークは使いません。架空のsidをルートと/appに分け、/applicationには/app用を送らないこと、無関係なDomainの拒否、Secureと期限切れの非送信を確認します。同名Cookieを辞書に平坦化せず、domain/pathで指定して更新・削除します。
HttpOnlyとSameSite=Strict付きのCookieもPythonから参照・送信できます。これはブラウザの制御が効かないことを示すだけで、ブラウザのSameSiteやCSRF対策を試験した結果ではありません。Secure付きはHTTPでは送られませんが、本例はTLS接続の検証をしません。実運用でTLS証明書検証を無効化しないでください。
次の3ファイルを同じディレクトリへ置きます。Requestsが導入済みの隔離環境でdemo.pyとpersistence.pyを実行してください。fixture.pyは認証サーバーではなく、固定した架空Cookieを発行して受信内容を返すだけです。timeoutとリダイレクト無効、環境プロキシ不使用により試験の範囲を絞っています。
fixture.py
"""Loopback-only synthetic Cookie fixture, not an authentication server."""
from contextlib import contextmanager
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from threading import Thread
class Handler(BaseHTTPRequestHandler):
def log_message(self, *args):
pass
def do_GET(self):
self.send_response(200)
if self.path == "/set":
for value in (
"sid=demo-root; Path=/; HttpOnly; SameSite=Strict",
"sid=demo-app; Path=/app",
"tls=demo-secure; Path=/; Secure",
"durable=demo-persistent; Path=/; Max-Age=3600",
"gone=demo-expired; Path=/; Max-Age=0",
"foreign=demo-rejected; Domain=example.invalid; Path=/",
):
self.send_header("Set-Cookie", value)
data = self.headers.get("Cookie", "").encode("ascii")
self.send_header("Content-Length", str(len(data)))
self.end_headers()
self.wfile.write(data)
@contextmanager
def loopback():
server = ThreadingHTTPServer(("127.0.0.1", 0), Handler)
server.daemon_threads = True
thread = Thread(target=server.serve_forever, kwargs={"poll_interval": 0.05})
thread.start()
try:
yield f"http://127.0.0.1:{server.server_port}"
finally:
server.shutdown()
server.server_close()
thread.join(timeout=2)
if thread.is_alive():
raise RuntimeError("Fixture failed to stop")
demo.py
"""Run from this directory: python -I -B -c ... (see README)."""
import requests
from requests.cookies import CookieConflictError
from fixture import loopback
def exercise(base):
with requests.Session() as session:
session.trust_env = False
def get(path, **kwargs):
response = session.get(base + path, timeout=(2, 2),
allow_redirects=False, **kwargs)
response.raise_for_status()
return response
first = get("/set")
assert first.request.headers.get("Cookie") is None
assert len(list(first.cookies)) == 4
root = get("/echo")
app = get("/app/echo")
assert "sid=demo-root" in root.text
assert "sid=demo-app" not in root.text
assert "sid=demo-app" in app.text and "sid=demo-root" in app.text
assert "sid=demo-app" not in get("/application").text
for name in ("tls=", "gone=", "foreign="):
assert name not in app.text
assert len(list(root.cookies)) == 0
assert len(list(session.cookies)) == 4
assert session.cookies.get("sid", domain="127.0.0.1", path="/") == "demo-root"
try:
session.cookies.get("sid")
except CookieConflictError:
pass
else:
raise AssertionError("Ambiguous name must not silently select a value")
assert "once=demo-once" in get("/echo", cookies={"once": "demo-once"}).text
assert "once=" not in get("/echo").text
session.cookies.set("kept", "demo-kept", domain="127.0.0.1", path="/")
assert "kept=demo-kept" in get("/echo").text
session.cookies.set("sid", "demo-updated", domain="127.0.0.1", path="/")
session.cookies.clear("127.0.0.1", "/app", "sid")
assert "sid=demo-updated" in get("/app/echo").text
assert "sid=demo-app" not in get("/app/echo").text
# No values or Cookie headers are logged, even for synthetic data.
return "scope, duplicates, response jar and one-shot checks passed"
if __name__ == "__main__":
with loopback() as url:
print(exercise(url))
架空値だけのLWP保存
既定では永続Cookieだけ、明示した試験ではセッションCookieも保存し、load側でもignore_discardを指定します。ignore_expiresはFalseのままです。期限切れをTrueで復活させてもサーバーの認証有効性は復元されません。破損・アクセス不能時は処理を中止し、既存ファイルを空jarで上書きしません。
保存対象は専用fixtureの架空値だけです。専用一時ディレクトリを使い、POSIXではディレクトリ0700・ファイル0600と排他新規作成を使います。Windowsではchmodの数値をACLの保証とみなせないため、アクセス権を別途確認してください。本例は安全な実資格情報ストア・原子的な保存・クラッシュ復旧の実装ではなく、実セッションの平文保存を推奨しません。
persistence.py
"""Plaintext LWP serialization test: ONLY the local fixture's fake cookies."""
import os
from http.cookiejar import LWPCookieJar, LoadError
from pathlib import Path
from tempfile import TemporaryDirectory
import requests
from fixture import loopback
def load_checked(path, *, include_session=False):
jar = LWPCookieJar(str(path))
try:
jar.load(ignore_discard=include_session, ignore_expires=False)
except LoadError:
raise RuntimeError("Invalid cookie fixture file; stop without overwriting") from None
except OSError:
raise RuntimeError("Cookie fixture file unavailable; stop") from None
return jar
def save_new(jar, path, *, include_session=False):
# Exclusive creation in a private temporary directory, never reuse a real jar.
fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
os.close(fd)
try:
jar.save(str(path), ignore_discard=include_session, ignore_expires=False)
except OSError:
raise RuntimeError("Cookie fixture save failed; stop") from None
def exercise_persistence(base):
with TemporaryDirectory(prefix="cwiki-fake-cookies-") as directory:
if os.name == "posix":
os.chmod(directory, 0o700)
jar = LWPCookieJar()
with requests.Session() as session:
session.trust_env = False
session.cookies = jar
response = session.get(base + "/set", timeout=(2, 2), allow_redirects=False)
response.raise_for_status()
durable = Path(directory) / "durable.lwp"
all_fake = Path(directory) / "session-demo.lwp"
save_new(jar, durable)
save_new(jar, all_fake, include_session=True)
assert {c.name for c in load_checked(durable)} == {"durable"}
assert {c.name for c in load_checked(all_fake)} == {"durable"}
restored = load_checked(all_fake, include_session=True)
assert len(list(restored)) == 4
assert all(not c.is_expired() for c in restored)
return "LWP persistent/session round-trip checks passed"
if __name__ == "__main__":
with loopback() as url:
print(exercise_persistence(url))
値の設定と取得:SimpleCookie
元の最小例は from http import cookies の後に C = cookies.SimpleCookie()、C["flag"] = "0" と設定し、flag = C["flag"].value で値を取得するものです。これはPython内の文字列操作で、Requestsの送信jarとは別です。入力解析ではCookieErrorと必要キーの欠落も扱い、Cookieの値を認証済みの事実とみなしません。
公式資料と検証範囲
確認日:2026年10月26日。Requests Session、Requests Cookie実装、Python 3.12 http.cookiejar、Python 3.12 http.cookies、RFC 6265、MDN Set-Cookie。ブラウザ互換性、実認証、TLS、CSRF対策、OSのACLはこのローカル試験の保証対象ではありません。
子ページはありません
人気ページ
- 1 Eclipseで「サーバーに追加または除去できるリソースがありません。」の原因と対処法
- 2 tomcat の起動 / 停止ログと catalina.log・catalina.out の違い
- 3 JavaScript で base URL を取得する方法|window.location.origin
- 4 YouTube Data API v3 エラー一覧|403・400・404 の原因と対処
- 5 Laravel エラー一覧|500/Blade/DB 接続/ルーティングの代表エラー
- 6 3Dグラフィックスのソフト一覧|Blender・Metasequoiaと用途別の選び方
- 7 Spring Frameworkのアノテーション一覧
- 8 【Spring】@Valueアノテーションとは
- 9 CATALINA_HOME の確認方法 (Linux / Mac)
- 10 【Spring】@Autowiredアノテーションとは
最近更新/作成されたページ
- Visual Studio「デザイナーを表示できません」エラーの原因と対処 2026-10-28 12:06:00
- SSH 認証に失敗しました。再試行してください の対処 2026-10-28 12:06:00
- SSH2 秘密鍵の読み込みに失敗しました の原因と対処 2026-10-28 12:06:00
- TeraTerm のよくあるエラーと対処法完全ガイド — Connection refused / 2026-10-28 12:06:00
- Visual Studio エラー一覧|コンパイルエラー・警告の確認方法と対処 2026-10-28 11:39:57
- Whitelabel Error Page の原因と消し方|Spring Boot のエラーページ 2026-10-28 11:39:57
- Eclipseで「サーバーに追加または除去できるリソースがありません。」の原因と対処法 2026-10-28 11:39:57
- SQL WHERE 句完全ガイド 2026-10-28 11:39:57
- SQL JOIN 完全ガイド (INNER/LEFT/RIGHT/FULL OUTER) 2026-10-28 11:39:57
- tomcat の起動 / 停止ログと catalina.log・catalina.out の違い 2026-10-28 10:24:23
- Tomcat の起動確認方法 5 つ|localhost:8080・ポート・プロセス・ログ 2026-10-28 10:24:23
- Oracle 19c COMMITの使い方: 暗黙コミット・自動確定・応答不明時の確認 2026-10-28 09:59:09
- Oracle 19c DELETE文|削除対象の確認・COMMIT前の判断・ROLLBACKの条件 2026-10-28 09:59:09
- Oracle 19c INSERT文|INSERT ALL・SELECT・RETURNINGとMERGEの使い分け 2026-10-28 09:59:09
- Oracle 19cのトランザクション制御|COMMIT・ROLLBACK・分離レベルと接続設定 2026-10-28 09:59:09