A cookie decoder that reads headers the way browsers do
This cookie decoder takes the raw text of a Set-Cookie response header, a Cookie request header or the string from document.cookie and turns it into a clear table: every cookie’s name and value, when it expires, which domain and path it belongs to, and whether it is Secure, HttpOnly, SameSite or Partitioned. Parsing follows the algorithm in RFC 6265 section 5.2, including its forgiving date parser, so you see what a browser would actually store.
Values are decoded too. URL-encoding is reversed, Base64 and Base64URL are decoded when they hold text or JSON, JSON Web Tokens show their header and claims, and signed cookies from Flask (itsdangerous), Django, Express and Ruby on Rails are split into payload, timestamp and signature. Encrypted cookies are identified but never “cracked”.
Each cookie gets plain-English findings: browsers rejecting SameSite=None without Secure, broken __Host- and __Secure- prefixes, login cookies that JavaScript can read, lifetimes beyond the 400-day cap and attributes that are silently ignored. Everything runs locally — your session tokens are not sent anywhere.
How to use the Cookie Decoder
- Copy the headers from your browser’s network panel (Response Headers or Request Headers) or from
curl -i https://example.com, or rundocument.cookiein the console. - Paste them into Headers or cookie string. The status line and other headers are skipped automatically; choose an Input type only if detection guesses wrong.
- Scan the summary table for each cookie’s decoded type, lifetime and flags.
- Open a cookie card to read its findings, the meaning of every attribute and each decoding step of its value.
- Use Copy as JSON to paste the parsed result into a ticket or test.
Cookie Decoder features
Browser-accurate parsing
Implements RFC 6265’s Set-Cookie and cookie-date algorithms: RFC 1123, RFC 850 two-digit years and asctime dates all work, invalid attributes are ignored exactly as browsers ignore them.
Framework-aware value decoding
Recognises JWTs, Flask and Django signed data (including zlib-compressed payloads), Express s: signatures, Rails signed and AES-GCM encrypted cookies, Google Analytics IDs and common session names.
Security findings
Flags prefix violations, missing Secure or HttpOnly on login cookies, SameSite and Partitioned rules, oversize cookies, the 400-day lifetime cap and deletion headers.
Handles messy input
Accepts several Set-Cookie lines, comma-joined headers, Cookie headers with duplicate names and raw document.cookie strings.
Private
Parsing and decoding use only JavaScript in this page, so real session cookies never leave your device.
What each Set-Cookie attribute does
| Attribute | Effect | Common mistake |
|---|---|---|
| Expires / Max-Age | Makes the cookie persistent; Max-Age (seconds) wins over Expires (a date) | Expecting more than 400 days — browsers cap it |
| Domain | Shares the cookie with all subdomains | Setting it “to be safe” — leaving it out is stricter |
| Path | Limits which URLs receive the cookie | Treating it as a security boundary; it is not one |
| Secure | Only sent over HTTPS | Omitting it on session cookies |
| HttpOnly | Hidden from JavaScript | Omitting it on login cookies, exposing them to XSS |
| SameSite | Strict, Lax or None — when cross-site requests carry the cookie | None without Secure, which is rejected |
| Partitioned | Separate third-party cookie per top-level site (CHIPS) | Forgetting Secure |
Signed is not the same as encrypted
Many frameworks keep session data in the cookie itself. Flask, Django’s signed-cookie sessions and older Rails versions sign it: the payload is plain Base64 that anyone can read, and the HMAC only stops it being changed. That is why this decoder can show a Flask session’s contents without any key — and why secrets must never be placed in one. Rails 5.2 and later, Laravel and ASP.NET Core encrypt their cookies, so all you can learn without the server’s key is the format and size.
Worked example: the Flask cookie eyJ1c2VyX2lkIjo0Mn0.ZVPxAA.LIr3YcHFjdrdtQSuFvBzSarrgwM splits at the dots into the payload {"user_id":42}, the timestamp bytes 65 53 F1 00 (Unix time 1700000000, 14 November 2023 22:13:20 UTC) and a 20-byte HMAC-SHA1 signature. To inspect a JWT’s signature and claims in more depth, use the JWT Decoder; for single values, the URL Encoder and Base64 Encoder work too.
Reading the findings
Red findings mean a browser will refuse the cookie or that it is seriously exposed; amber findings are risky defaults; blue notes explain behaviour such as host-only cookies or deletion headers. When you are ready to write a corrected header, the Cookie Generator builds one with the right attributes for your framework.
Cookie Decoder FAQ
How do I decode a cookie value?
Paste the cookie or the whole Set-Cookie header into the cookie decoder. It reverses URL-encoding, decodes Base64 or Base64URL text and JSON, and recognises JWT, Flask, Django, Express and Rails formats, showing each step separately so you can see how the value was built.
Can this tool decrypt an encrypted session cookie?
No, by design. Encrypted cookies from Rails, Laravel or ASP.NET Core can only be opened with the application’s secret key. The tool identifies the format and reports its size; it never attempts to guess keys.
Why does the browser ignore my SameSite=None cookie?
Chromium-based browsers require SameSite=None cookies to be Secure as well. Add Secure and serve the site over HTTPS; the decoder marks this case in red.
What is the difference between Cookie and Set-Cookie?
Set-Cookie is sent by the server in a response and carries the value plus attributes such as Expires and SameSite. Cookie is sent by the browser in each request and carries only name=value pairs — the attributes stay inside the browser.
Are my cookies sent to a server when I use this decoder?
No. The page parses and decodes everything with JavaScript in your browser. That matters, because a session cookie works like a password until it expires.
Why do I see the same cookie name twice in a Cookie header?
Two cookies may share a name if they were set with different Path or Domain values. The browser sends both, listing the one with the longer path first, and most server frameworks read only the first.