How to check cookie flags with curl

Two different reads. curl -s -o /dev/null -D - URL prints the Set-Cookie lines the server sent, attributes included. curl -c jar.txt URL writes what curl decided to keep, and -b jar.txt sends it back. The jar records Secure and HttpOnly in columns and drops SameSite completely.

Why check this

Reading cookies from the command line is how you take the browser out of a bug report. The browser shows you the cookies that survived its own rules, so a cookie it rejected is invisible in DevTools and looks like a server that never set it. curl shows the header as sent, which separates "the application did not set it" from "the browser threw it away". Run this the moment a login works for one tester and not another.

Prerequisites

Steps

  1. Step 1.

    Print the response headers and read what the server sent.

    curl -s -o /dev/null -D - 'https://httpbin.org/cookies/set?sid=abc123&theme=dark' | grep -i -E '^HTTP/|^set-cookie|^location'
    
    HTTP/2 302
    location: /cookies
    set-cookie: sid=abc123; Path=/
    set-cookie: theme=dark; Path=/

    Two cookies with Path and nothing else. No Secure, no HttpOnly, no SameSite. This is the raw header, before any client rule is applied.

  2. Step 2.

    Write the cookie jar to stdout with -c - instead of a filename.

    curl -s -o /dev/null -c - 'https://httpbin.org/cookies/set?sid=abc123&theme=dark'
    
    # Netscape HTTP Cookie File
    # https://curl.se/docs/http-cookies.html
    # This file was generated by libcurl! Edit at your own risk.
    
    httpbin.org	FALSE	/	FALSE	0	theme	dark
    httpbin.org	FALSE	/	FALSE	0	sid	abc123

    Seven tab-separated columns: host, whether subdomains are included, path, the Secure flag, the expiry as a Unix timestamp, the name, the value. 0 in the expiry column marks a session cookie.

  3. Step 3.

    Run step 2 again with -c jar.txt so the jar lands in a file, then send that file back.

    curl -s -b jar.txt https://httpbin.org/cookies
    
    {
    "cookies": {
      "sid": "abc123",
      "theme": "dark"
    }
    }

    -c writes, -b reads. Pass both on the same command to keep a session alive across several requests.

  4. Step 4.

    Point the jar at a host that sets real attributes, and read the flag columns without the values.

    curl -s -o /dev/null -c - https://www.cloudflare.com/ | grep -v '^#\s' | grep . | cut -f1,2,3,4,6
    
    #HttpOnly_.www.cloudflare.com	TRUE	/	TRUE	__cf_bm
    www.cloudflare.com	FALSE	/	TRUE	kndctr_8AD56F28618A50850A495FB6_AdobeOrg_identity
    #HttpOnly_www.cloudflare.com	FALSE	/	TRUE	cfz_adobe
    #HttpOnly_www.cloudflare.com	FALSE	/	TRUE	cfz_google-analytics_v4
    www.cloudflare.com	FALSE	/	FALSE	_ga

    HttpOnly becomes a #HttpOnly_ prefix on the host column, and Secure is the fourth column. SameSite appears nowhere, because the file format has no column for it.

  5. Step 5.

    Send a cookie you typed yourself, with no jar involved.

    curl -s -b 'sid=abc123; theme=dark' https://httpbin.org/cookies
    
    {
    "cookies": {
      "sid": "abc123",
      "theme": "dark"
    }
    }

    -b treats an argument containing = as cookie data and anything else as a filename. This is how you replay one value from a bug report without reproducing the login.

How to read the result

| What you see | What it means | What to do | | --- | --- | --- | | #HttpOnly_ prefix on the host column | The server sent HttpOnly | Nothing. This is the jar's way of recording it. | | TRUE in the fourth column | The server sent Secure | Nothing. Compare with the header if it surprises you. | | 0 in the fifth column | A session cookie with no expiry | It survives in the file, which is not what a browser does at close. | | A leading dot on the host, as in .www.example.com | The cookie was set with a Domain attribute | Check whether every subdomain should receive it. | | No SameSite anywhere in the file | The format cannot store it | Read the attribute from the Set-Cookie header instead. | | The jar is empty after a request that set a cookie | curl rejected it, usually a Domain that does not match | Compare with step 1. The header will show what was sent. |

Common mistakes

Sign: A SameSite audit is run from the cookie jar and every cookie comes back clean.Cause: The Netscape cookie file has seven columns and none of them is SameSite. Measured on 2026-09-11, a jar written from www.cloudflare.com recorded Secure and HttpOnly and stored nothing for the SameSite=None the header carried.
Sign: curl stores a cookie that the browser refuses to keep.Cause: curl applies its own matching rules and records the attributes without enforcing what they mean. A cookie sent as SameSite=None without Secure lands in the jar and is discarded by a browser, so the two tools disagree by design.
Sign: -j is added to clear state between runs and the session disappears.Cause: --junk-session-cookies discards every cookie with an expiry of 0 while loading the jar. Against httpbin.org the follow-up request returned an empty cookies object. Delete the jar file instead when you want a clean start.
Sign: -b jar.txt is used on every request and the session dies halfway through the flow.Cause: -b only reads. A server that rotates the session token on login sends a new cookie that is never written back, so the next request replays the old value. Pass -c with the same filename as well.
Sign: curl -I returns no Set-Cookie for a login endpoint.Cause: -I sends HEAD. A login route that only answers POST returns a method error, and the cookie is never issued. Use -o /dev/null -D - with the real method and body.

What to check next

FAQ

How to check the Set-Cookie header?

Step 1. -D - writes the response headers to stdout and -o /dev/null discards the body, so grep sees only headers. Add -L to include the headers of every hop in a redirect chain.

What is the cookie jar file format?

The Netscape format: one cookie per line, seven tab-separated fields, comment lines starting with #. HttpOnly is encoded as a #HttpOnly_ prefix on the host field, which is why those lines look like comments.

Why does the browser show fewer cookies than curl sent?

The browser drops cookies that break its rules, SameSite=None without Secure among them, and hides HttpOnly cookies from document.cookie. curl reports the header as received and leaves the rules alone.

Can I edit the jar by hand?

Yes, the file is plain text with tab separators. Keep the tabs. A jar edited with spaces is read as a malformed line and skipped without a warning.

How do I keep a session across several curl calls?

Pass -b jar.txt -c jar.txt on every request. Reading and writing the same file is what makes a rotated session token survive to the next call.

Verified

Verified by Maks Vernycurl 8.21.0

Each output block is what the command above it printed on that date, on the host named in the step. Figures read from a live site move between runs. Compare the shape of the answer rather than the digits, and see the methodology for how a page is re-verified.

basic5 minpublished updated Maks Verny