How to Secure Cookies in Web Applications
April 2026 - Abhishek Mardiya
Cookies often store sessions, refresh tokens, CSRF tokens, or other sensitive state. The problem is not cookies themselves. The problem is insecure cookie configuration.
A secure cookie usually comes down to scope and exposure:
- who can receive it
- where it is available
- how long it lives
- whether JavaScript can read it
Expires and Max-Age
These control cookie lifetime.
Expiressets an exact dateMax-Agesets a lifetime in seconds
For auth cookies, shorter lifetimes reduce risk if a cookie is stolen.
Set-Cookie: session=abc123; Max-Age=1800; Path=/; HttpOnly; Secure; SameSite=Lax
Domain
Domain decides which domains can access the cookie.
If you do not set it, the cookie stays on the current host, which is usually safer.
Domain=example.com
That shares the cookie with:
example.comapi.example.comwww.example.com
Only set Domain when you really need cross-subdomain sharing.
Path
Path limits where the cookie is sent.
Path=/dashboard
That sends the cookie only for routes under /dashboard.
Use Path=/ when the cookie should be available site-wide.
Secure
Secure means the cookie is only sent over HTTPS.
Set-Cookie: session=abc123; Secure
For session or auth cookies, this should be a default in production.
HttpOnly
HttpOnly prevents JavaScript from reading the cookie through document.cookie.
Set-Cookie: session=abc123; HttpOnly
This reduces the impact of XSS because injected scripts cannot easily steal the cookie.
SameSite
SameSite controls cross-site behavior.
Strict: only same-site requestsLax: same-site requests plus top-level navigationNone: all cross-site requests, and it must also useSecure
Set-Cookie: session=abc123; SameSite=Strict
For many session cookies, Lax or Strict is the right choice.
Safe Defaults
If a cookie stores authentication or sensitive state, start with:
HttpOnlySecureSameSite=LaxorSameSite=StrictPath=/- short
Max-Age - no
Domainunless needed
Common Mistakes
- Leaving session cookies readable from JavaScript by skipping
HttpOnly - Using
SameSite=Nonewithout actually needing cross-site requests - Setting a broad
Domainfor no reason - Giving sensitive cookies very long lifetimes
- Assuming
Pathalone makes a cookie secure
The goal is simple: reduce unnecessary exposure.
Next.js Example
If you are using the Next.js App Router, a clean way to set a secure session cookie is in a Route Handler.
import { NextResponse } from "next/server";
export const POST = async (): Promise<NextResponse> => {
const response: NextResponse = NextResponse.json({ ok: true });
response.cookies.set("session", "token-value", {
httpOnly: true,
secure: true,
sameSite: "strict",
maxAge: 60 * 60 * 24 * 30,
path: "/",
});
return response;
};
This example does a few important things correctly:
httpOnly: truekeeps the cookie out ofdocument.cookiesecure: trueensures it is only sent over HTTPSsameSite: "strict"prevents the cookie from being sent in cross-site requestsmaxAgegives the cookie a defined lifetimepath: "/"makes the cookie available across the app
If your app needs a slightly less strict default for normal navigation, sameSite: "lax" is often a practical choice. If you need cross-site requests, use sameSite: "none" and keep secure: true.
Final Takeaway
Securing cookies is about choosing the narrowest safe defaults. For most production apps, that means HttpOnly, Secure, a deliberate SameSite value, a reasonable lifetime, and the smallest scope possible.