🌐 Try the browser playground → · paste a Set-Cookie, see its security holes. Nothing is uploaded — it all runs client-side.
You set a session cookie and shipped it. But it went out without HttpOnly (so any
XSS can steal it), without Secure (so it rides plain HTTP), with SameSite=None
but no Secure (so the browser rejects it entirely), or with a __Host- prefix
whose strict rules it quietly violates — and now logins mysteriously don't stick.
You find out from a pentest, a console warning, or a 3 a.m. incident.
cookie-doctor lints a Set-Cookie for these problems locally and
deterministically — from a string, a curl -I response, or a config file. It
knows the __Host-/__Secure- prefix rules and the SameSite=None↔Secure
interaction, the exact spec people get wrong.
npx cookie-doctor scan -c "sid=abc; SameSite=None"sid 46/100 (F)
✗ SameSite=None without Secure — the browser rejects the cookie [SameSite]
✗ No HttpOnly — this looks like a session cookie, JS can read it [HttpOnly]
✗ No Secure — sent over plaintext HTTP [Secure]
- 🧨 It catches the "silently dropped" bugs.
__Host-withoutSecure, with aDomain, or withoutPath=/;SameSite=NonewithoutSecure— the browser rejects these cookies outright, so your session just doesn't work. cookie-doctor flags them as validity errors, not style nits. - 🎯 Severity that knows what a session cookie is. A missing
HttpOnlyonsid/auth/a JWT-valued cookie is an error; ontheme=darkit's a gentle warning. No noise. - 🔒 Local & deterministic. No website, no API key, runs offline and in CI. Same cookie → same result. Fail the PR that ships an insecure session cookie.
- 🧩 Reads it from anywhere. A raw
Set-Cookie, acurl -Idump, an nginxadd_header, an ApacheHeader set, orvercel.json.
Why not paste it into an LLM? The __Host-/__Secure- rules and the
SameSite=None/Secure interaction are exact spec a chatbot gets subtly wrong —
and you want this gating session config on every PR, not once.
# run it now
npx cookie-doctor scan -c "<your Set-Cookie>"
# or add it
npm install -g cookie-doctor # global CLI
npm install -D cookie-doctor # CI dependencyNode ≥ 18. The core is dependency-free and browser-safe.
cookie-doctor scan -c "__Host-sid=abc; Secure; HttpOnly; SameSite=Lax; Path=/"
curl -sI https://example.com | cookie-doctor scan # straight from a response
cookie-doctor scan headers.txt _headers vercel.json # from configs
cookie-doctor scan -c "<cookie>" --min-score 80 # CI gate
cookie-doctor scan headers.txt --md cookies.md # Markdown report
cookie-doctor init # write a configSee examples/sample-report.md, and
examples/strong.cookie.txt for a cookie that
scores 100.
| Group | Examples |
|---|---|
| Will be dropped by the browser | __Host-/__Secure- prefix rules (Secure required, no Domain, Path=/), SameSite=None without Secure, invalid SameSite, oversized (> 4 KB) |
| XSS / theft | missing HttpOnly (error on session/token cookies) |
| CSRF | missing SameSite |
| Transport | missing Secure |
| Scope | Domain shared with subdomains, legacy leading-dot Domain |
| Lifetime | long-lived auth cookies (configurable threshold) |
Each finding is a weighted error / warning / info; the cookie rolls up to a 0–100 score and an A–F grade you can gate in CI.
1. Gate session-cookie security in CI. A PR that adds an auth cookie without
HttpOnly/Secure, or breaks a __Host- rule, fails the build:
# .github/workflows/ci.yml
- run: curl -sI http://localhost:3000/login | npx cookie-doctor scan --min-score 902. Audit what your framework actually sends. Pipe a real response through it —
express-session, NextAuth, a reverse proxy — and see the attributes you thought
were set.
3. Triage a security finding. A scanner said "insecure cookie" — cookie-doctor
tells you which attribute and why, with the exact fix.
cookie-doctor init writes cookie-doctor.config.json:
import { analyzeSetCookie, DEFAULT_CONFIG } from "cookie-doctor";
const report = analyzeSetCookie("inline", "sid=abc; Secure", DEFAULT_CONFIG, Date.now());
for (const f of report.findings) console.log(f.severity, f.rule, f.attribute);Also exported: parseSetCookie, checkCookie, extractCookies, lifetimeSeconds,
and types. The core has zero runtime dependencies.
- 🤖 Optional
--ailayer (bring-your-own key) to explain a cookie's risk in context / suggest a hardened header. The core stays 100% offline and deterministic. Partitioned(CHIPS) attribute awareness.- Read cookies from a saved
.haror a browser cookie export. - ✅ A browser playground — paste a
Set-Cookie, see the audit, nothing uploaded. Live here.
cookie-doctor is free and MIT-licensed, built and maintained in spare time. If it caught an insecure cookie before your users did, please consider supporting it:
- ⭐ Star this repo — the simplest free way to help others find it.
- 🍋 Sponsor via Lemon Squeezy — one-time or recurring.
MIT © cookie-doctor contributors
{ "ignore": [], // rule ids to skip "sessionNames": ["session", "sid", "auth", "token", "jwt", "..."], "maxAgeDays": 30, // warn above this lifetime "minScore": 0 // CI gate threshold }