How to check if cookies are secure and HttpOnly

Print the response headers and read every Set-Cookie line with curl -s -o /dev/null -D - https://example.com/, piped to grep -i set-cookie. A session cookie is covered when its line carries both attributes. Secure keeps it off plain HTTP requests, and HttpOnly keeps it out of document.cookie.

Why check this

The two attributes defend against two different attacks and neither substitutes for the other. Without Secure, the cookie is attached to the first plain HTTP request a user makes, which happens before the redirect to HTTPS lands. Without HttpOnly, one stored cross-site scripting payload reads the session and posts it somewhere else. Run this check on every response that sets a cookie after a login, and repeat it after any change to the authentication service or the CDN in front of it.

Prerequisites

Steps

  1. Step 1.

    Print the name and the security attributes of every cookie the response sets, with the values and the lifetime removed.

    curl -s -o /dev/null -D - https://www.cloudflare.com/ | grep -i '^set-cookie:' | sed -E 's/^[Ss]et-[Cc]ookie: ([^=]+)=[^;]*/\1/' | sed -E 's/ ?(Expires|Path|Domain|Max-Age)=[^;]*;?//gI'
    
    _ga;
    cfz_google-analytics_v4; HttpOnly; SameSite=Lax; Secure
    cfz_adobe; HttpOnly; SameSite=Lax; Secure
    kndctr_8AD56F28618A50850A495FB6_AdobeOrg_identity; Secure; SameSite=Lax
    __cf_bm; HttpOnly; SameSite=None; Secure

    Five cookies, one line each, in the order the server sent them. Anything after the name is an attribute you have to justify.

  2. Step 2.

    Filter that list down to the cookies that are missing Secure.

    curl -s -o /dev/null -D - https://www.cloudflare.com/ | grep -i '^set-cookie:' | sed -E 's/^[Ss]et-[Cc]ookie: ([^=]+)=[^;]*/\1/' | sed -E 's/ ?(Expires|Path|Domain|Max-Age)=[^;]*;?//gI' | grep -vi 'Secure'
    
    _ga;

    One cookie, set by the analytics script rather than the application. That is the judgement call this step produces: a name to check against what it holds.

  3. Step 3.

    Run the same filter for HttpOnly.

    curl -s -o /dev/null -D - https://www.cloudflare.com/ | grep -i '^set-cookie:' | sed -E 's/^[Ss]et-[Cc]ookie: ([^=]+)=[^;]*/\1/' | sed -E 's/ ?(Expires|Path|Domain|Max-Age)=[^;]*;?//gI' | grep -vi 'HttpOnly'
    
    _ga;
    kndctr_8AD56F28618A50850A495FB6_AdobeOrg_identity; Secure; SameSite=Lax

    The second cookie has Secure and no HttpOnly. It travels only over TLS and any script on the page can still read it.

  4. Step 4.

    Repeat step 1 against the http:// URL, because a cookie set before the redirect is a separate cookie.

    curl -s -o /dev/null -D - http://www.cloudflare.com/ | grep -i '^set-cookie:' | sed -E 's/^[Ss]et-[Cc]ookie: ([^=]+)=[^;]*/\1/' | sed -E 's/ ?(Expires|Path|Domain|Max-Age)=[^;]*;?//gI'
    
    __cf_bm; HttpOnly;

    The same cookie name that carried Secure and SameSite=None over HTTPS is set here with HttpOnly alone. The 301 response sets it before any TLS is involved.

  5. Step 5.

    Open the site in Chrome, press F12, then DevTools, Application tab, Storage, Cookies, and select the origin.

    The grid has one column per attribute. HttpOnly and Secure are tick boxes, so a whole origin reads in one glance. To confirm HttpOnly from the other side, open the Console tab and enter document.cookie: every cookie marked HttpOnly is absent from the string that comes back, which is exactly the protection the attribute buys.

How to read the result

| What you see | What it means | What to do | | --- | --- | --- | | sid; HttpOnly; Secure on a session cookie | Both defences are on | Nothing. Move on to the SameSite attribute. | | Session cookie with Secure and no HttpOnly | Any script on the page can read the session | Fix it. One XSS finding becomes account takeover. | | Session cookie with HttpOnly and no Secure | The cookie rides on the first plain HTTP request | Fix it. The redirect to HTTPS happens after that request. | | Neither attribute on an analytics cookie | Normal for a third-party script | Record it and check what the value holds before waving it through. | | A cookie set on the http:// response | It exists before TLS starts | Treat its contents as public. Nothing secret belongs there. | | __Host- prefix on the name | The browser enforces Secure, host-only and Path=/ | Nothing. This is the strongest shape available. |

Common mistakes

Sign: The homepage is audited and every cookie looks fine, so the check passes.Cause: The session cookie is set by the login response, not by the homepage. A logged-out page sets analytics cookies, which are the ones most likely to look clean and matter least.
Sign: A cookie carries Secure, so it is recorded as safe from scripts.Cause: Secure controls the transport only. It says nothing about who can read the cookie inside the page. The attribute that blocks document.cookie is HttpOnly, and the two are set independently.
Sign: The cookie is HttpOnly, and the tester concludes cross-site scripting cannot reach the account.Cause: HttpOnly stops the value being read. A script running on the page can still send requests that carry the cookie, so the session is usable without ever being stolen.
Sign: DevTools shows Secure on a cookie that curl reported without it.Cause: Two different responses set the same name. Measured on 2026-09-11, www.cloudflare.com set __cf_bm with HttpOnly only on the HTTP 301 and with HttpOnly, Secure and SameSite=None on the HTTPS response. The browser shows the surviving one.

What to check next

FAQ

How to check if a cookie is HttpOnly?

Step 3 answers it from the server side. From the browser side, enter document.cookie in the Console: an HttpOnly cookie never appears in that string, while DevTools still lists it under Application, Cookies.

How to check the Secure flag on a cookie?

Step 2. The filter prints the cookies that lack it, which is shorter to read than the full list and is the answer you act on.

How to check cookies in browser DevTools?

Application tab, Storage, Cookies, then pick the origin. The grid shows one row per cookie with tick boxes for HttpOnly and Secure, so one screen covers the whole origin.

Does curl enforce the Secure attribute?

No. Against a local server on http://127.0.0.1, curl accepted a cookie marked Secure and wrote it to the jar with the secure column set to TRUE. curl records the attributes and leaves the rules to the browser.

Should analytics cookies carry both attributes?

They should carry Secure. HttpOnly is often impossible because the analytics script reads the value itself, which is a reason to keep anything identifying out of it.

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