Secure Cookie Flags: HttpOnly, Secure and SameSite for NZ Sites
What the HttpOnly, Secure and SameSite cookie flags do, why each matters under the NZ Privacy Act 2020, and how to set them in nginx, Apache and Express.
Quick answer
Every cookie your site sets should carry three flags: Secure (only ever sent over HTTPS), HttpOnly (not readable by JavaScript), and SameSite=Lax (not sent on cross-site requests). Together they stop the three common ways cookies are stolen or replayed in cross-site request forgery (CSRF) attacks. A scanner cookies finding from the Free Website Security Check flags any of these as missing — paste the snippet for your stack and restart the service.
If you are running your site behind a CDN or reverse proxy, set the flags at the application layer (where the cookie is created); setting them at the proxy usually does not work for Set-Cookie headers (1).
What cookies do and what the flags protect
A cookie is a short piece of state your server asks the browser to remember. On every subsequent request, the browser sends it back. This is how your site knows who you are across page loads.
Without flags, a cookie is just a string. With the right three flags, it becomes much harder to steal or misuse:
| Flag | What it stops | Plain-English behaviour |
|---|---|---|
| Secure | Cookie theft over plain HTTP | The browser refuses to send the cookie unless the connection is HTTPS |
| HttpOnly | Cookie theft via XSS (cross-site scripting) | JavaScript on the page cannot read the cookie via document.cookie |
| SameSite=Lax (or Strict) | CSRF (cross-site request forgery) | The browser does not send the cookie when the request originates from a different site |
A reasonable baseline for a NZ school or small-business site is to ship all three. The next sections explain what each flag does in detail and why it matters in NZ context.
The Secure flag
Secure tells the browser: never send this cookie back unless the connection is HTTPS. Without it, a coffee-shop network attacker can briefly force the visitor over plain HTTP and read the cookie in transit (1).
Secure does not require HSTS to be set, but HSTS makes it much harder to bypass — once a browser has seen HSTS, it silently rewrites any http:// URL to https://, which means a downgrade attack on a Secure cookie becomes almost impossible. See the HSTS guide for the connection-side details.
Secure is not the same as the padlock icon in the browser address bar. The padlock only tells you the page itself loaded over HTTPS. A cookie without the Secure flag can still be sent over plain HTTP on the next request, even if the page that set it was loaded over HTTPS (1).
The HttpOnly flag
HttpOnly tells the browser: do not let JavaScript on the page read this cookie. Without HttpOnly, any script running on your page — including a malicious script injected via a cross-site scripting (XSS) bug — can read the cookie and send it to an attacker (2).
For session cookies and authentication tokens, the right default is HttpOnly. There is almost never a legitimate reason for a page script to read these cookies; the cookie is for the server, not the client.
If your front-end JavaScript legitimately needs to read a cookie (for example, to display the logged-in user's name), use a separate, non-sensitive cookie without HttpOnly and keep the session cookie protected.
The SameSite attribute
SameSite tells the browser when the cookie should be sent on cross-site requests. The valid values are Strict, Lax, and None:
SameSite=Strict— never sent on any cross-site request, including top-level navigationsSameSite=Lax— sent on top-level navigations (clicking a link), but not on subresource requests (images, iframes, fetch from a third party)SameSite=None— always sent; must also setSecure
Lax is the right default for most NZ sites. It blocks the common CSRF attack (a malicious site triggering a request to your bank on behalf of a logged-in user) without breaking the user experience of clicking through to your site from email or a search result (1).
SameSite=Lax for cookies that have no SameSite attribute at all, but only as of recent versions. Older browsers, embedded webviews, and some school MIS portals still send cookies without it. Setting it explicitly is the only way to be sure (1).
Why this matters for New Zealand sites under the Privacy Act 2020
Cookies carry identity. A stolen session cookie is functionally equivalent to a stolen password — the attacker becomes the user until the cookie expires or is revoked. That identity often links back to student data, staff records, or payment details, all of which sit inside the Privacy Act 2020 (3).
Two specific NZ angles:
- School portals and student records. A school portal that handles enrolments, attendance, or pastoral notes uses cookies for the login session. If those cookies lack
SecureandHttpOnly, a single XSS bug anywhere on the school site is enough to exfiltrate them. That is a notifiable privacy breach under the Act (3). - Public-sector SaaS contracts. If your site vendors are processing data on behalf of a school, their cookie handling is part of your due diligence. CERT NZ names session management as a baseline control (4).
For the broader educator context (devices, accounts, phishing), see Building Cyber-Safe Habits as an NZ Educator.
Copy-paste fixes for the four common stacks
The exact way to set the flags depends on how your site creates cookies. The snippets below cover the most common stacks.
Express (Node.js) with cookie-parser
This is the most common case for modern NZ small-business sites:
import cookieParser from 'cookie-parser';
app.use(cookieParser());
app.get('/login', (req, res) => {
res.cookie('session', token, {
httpOnly: true,
secure: true,
sameSite: 'lax',
path: '/',
maxAge: 60 * 60 * 1000, // 1 hour
});
res.send('logged in');
});
If you are using Express session middleware (express-session), set the flags there instead:
app.use(session({
cookie: {
httpOnly: true,
secure: true,
sameSite: 'lax',
maxAge: 60 * 60 * 1000,
},
// ... other session config
}));
Django
In settings.py:
SESSION_COOKIE_SECURE = True
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SAMESITE = 'Lax'
CSRF_COOKIE_SECURE = True
CSRF_COOKIE_HTTPONLY = True
CSRF_COOKIE_SAMESITE = 'Lax'
These four settings cover the session cookie and the CSRF token cookie, which together handle almost all authentication on a Django site.
nginx (reverse proxy)
You usually cannot set cookie flags in nginx directly — the application sets the Set-Cookie header and nginx just passes it through. The exception is proxy_cookie_path, which lets you tweak the path:
proxy_cookie_path / "/; Secure; HttpOnly; SameSite=Lax";
Note: this trick is brittle. It is better to set the flags in the application where the cookie is created. The snippets above for Express and Django are the reliable path.
Apache
Like nginx, Apache usually only passes cookies through. If you must rewrite them at the proxy level, mod_headers can do it:
Header always edit Set-Cookie "^([^;]+)(.*)$" "$1$2; Secure; HttpOnly; SameSite=Lax"
Treat this as a safety net, not a substitute for setting the flags at the application layer. Application-level configuration is auditable; rewrite rules are easy to miss in code review.

Common cookie flag mistakes to avoid
From running the free scanner across New Zealand school and small-business sites, the most frequent missteps are:
- Setting
Securebut noSameSite. WithoutSameSite, the cookie is still sent on cross-site requests, leaving CSRF open.Securealone is not enough (1). - Setting
SameSite=Stricton an SSO cookie. If your school uses single sign-on (Google Workspace, Microsoft 365),Strictcan break the initial login from a third-party link.Laxis usually the safer default (1). - Forgetting
Secureon the CSRF cookie. Django and similar frameworks set a separate CSRF cookie. If the session cookie isSecurebut the CSRF cookie is not, the CSRF token can leak over plain HTTP (5). - Setting the flags at the proxy and not the app. Reverse-proxy rewrites are easy to forget. Set them at the application layer first; the proxy only as a defence-in-depth measure.
- Not setting
Path=/. If your cookie has a narrowPath(e.g./admin), it may not be sent on requests outside that path, breaking login state. UsePath=/unless you have a specific reason not to.
Verify before you ship
Two ways to confirm the flags are live:
1. Run the free NZAI scanner
The Free Website Security Check flags each cookie missing any of the three flags as a cookies finding, and tells you which snippet to paste.
2. Browser DevTools
Open your site in Chrome or Firefox, log in, press F12, switch to the Application tab, click Cookies in the left pane, and inspect each cookie. You should see Secure, HttpOnly, and SameSite all ticked.
3. curl from the command line
curl -sI https://yoursite.nz | grep -i set-cookie
You should see the flags appended to each Set-Cookie header. To bypass cache, append a fresh query string (?nocache=1) or test against a URL the cache has not seen.
Knowledge check
Test your understanding of secure cookie flags with these questions. Click or tap each card to flip it and see the answer.
Sources and references
[1] Mozilla. (2025). MDN Web Docs — Set-Cookie header. https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie
[2] OWASP Foundation. (2025). Cross-Site Scripting (XSS) Prevention Cheat Sheet. https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html
[3] New Zealand. Parliament. (2020). Privacy Act 2020. https://www.legislation.govt.nz/act/public/2020/0031/latest/whole.html
[4] CERT NZ. (2025). Critical controls for small organisations. https://www.cert.govt.nz/
[5] Mozilla. (2025). MDN Web Docs — SameSite cookies. https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite
[6] Internet Engineering Task Force. (2025). RFC 6265 — HTTP State Management Mechanism. https://datatracker.ietf.org/doc/html/rfc6265
What to do next
- Run the Free Website Security Check to confirm every cookie on your site now carries all three flags.
- For the connection-side piece that makes
Secureactually safe (downgrade attacks), see HSTS Strict-Transport-Security Explained for NZ Sites. - For the user-consent piece (when do you legally need a cookie banner in NZ), see the Cookie Consent Banners guide — Privacy Act 2020 expectations, banner UX rules, and copy-paste WordPress and HTML examples.
- For the broader educator safety pathway, including account security and phishing, see Building Cyber-Safe Habits as an NZ Educator.
- For keeping your TLS certificate valid and renewed (the complementary half of HTTPS), see SSL Certificate Management for NZ Sites.
- Web Server Hardening for NZ Sites — how to block exposed
/.env,/.git,/backup.zip,/phpinfo.phpand/server-statuspaths so they return clean 404 instead of 403 on nginx and Apache. - Phishing and Fake Login Forms for NZ Schools — how exposed-secret findings connect to phishing campaigns, and the three signals (wrong URL, cross-origin form action, missing security headers) that reveal a fake login form.
- Environment File Leaks Explained for NZ Sites — what to do if your
.envwas already exposed, including the rotation playbook and secrets manager options.