
Table of Contents
By Khimananda Oli | Last reviewed: August 2026
Session hijacking remains one of the most common entry points for attackers targeting web applications, yet many teams still deploy default cookie configurations that leave sessions exposed. Implementing secure session cookies with SameSite and HttpOnly attributes is the single most effective server-side control against cross-site scripting (XSS) token theft and cross-site request forgery (CSRF). This guide provides exact configuration patterns for Nginx, Node.js, and PHP frameworks to harden your authentication layer immediately.
Secure, HttpOnly, and SameSite=Strict (or Lax) flags on every Set-Cookie header. These attributes prevent JavaScript access, enforce HTTPS transmission, and block cross-origin cookie submission, effectively mitigating XSS session theft and CSRF attacks.How do you configure secure session cookies with SameSite and HttpOnly?
Setting these attributes correctly requires understanding both the HTTP header syntax and your application framework's session configuration. A common mistake I see during security audits is setting only one or two flags while assuming the others default to safe values — they don't. In 2026, browsers treat missing SameSite as Lax, but relying on implicit defaults is fragile and fails compliance checks for standards like SOC 2 or ISO 27001.
The Three Required Attributes
- HttpOnly: Prevents client-side scripts from accessing the cookie via
document.cookie. Even if an attacker injects malicious JavaScript through an XSS vulnerability, they cannot read the session token. - Secure: Restricts cookie transmission to HTTPS connections only. This blocks passive network attackers on public Wi-Fi or compromised infrastructure from intercepting session tokens in transit.
- SameSite: Controls when browsers send cookies with cross-site requests.
Strictnever sends cookies cross-origin;Laxallows top-level GET navigations but blocks POST forms and AJAX from external sites.
For teams managing infrastructure alongside application code, remember that reverse proxies can override or append cookie attributes. If you're running Nginx in front of your app, check the Nginx installation and configuration guide to ensure proxy headers don't strip security flags before they reach the browser.
Raw Set-Cookie Header Syntax
Set-Cookie: session_id=abc123def456; HttpOnly; Secure; SameSite=Strict; Path=/; Max-Age=3600 Every attribute matters. Omitting Path=/ can cause session inconsistencies across routes. Skipping Max-Age creates persistent cookies that survive browser restarts, increasing the window for token compromise. Always set explicit expiration aligned with your security policy.
What is the difference between SameSite Strict and Lax for session cookies?
Choosing between Strict and Lax involves balancing security against user experience. This decision directly impacts how users interact with links from emails, chat apps, and external dashboards.
| Attribute Value | Same-Origin Request | Top-Level GET Navigation | Cross-Site POST / AJAX | Use Case |
|---|---|---|---|---|
| Strict | Sent | Not sent | Not sent | Banking, admin panels, high-security apps |
| Lax | Sent | Sent | Not sent | SaaS platforms, e-commerce, social apps |
| None | Sent | Sent | Sent (requires Secure) | Cross-domain auth, embedded widgets only |
In practice, Lax is the right default for most applications in 2026. It prevents CSRF on state-changing operations while preserving expected behavior when users click login links from email or Slack. Reserve Strict for systems where any cross-site session resumption would be catastrophic — financial dashboards, healthcare portals, or internal tooling accessed exclusively via direct navigation.
A frequent pitfall: using SameSite=None without also setting Secure. Modern browsers reject this combination outright, silently dropping the cookie. If you genuinely need cross-site cookies, always pair None with Secure and implement separate CSRF token validation since SameSite protection no longer applies.
How do you set secure cookie attributes in Nginx, Express, and Laravel?
Framework abstractions make cookie configuration convenient but often hide insecure defaults. Always verify the actual Set-Cookie header in production using browser dev tools or curl -I. Below are battle-tested configurations I use across client environments.
Nginx Reverse Proxy
When Nginx sits in front of your application, it can enforce cookie security regardless of backend behavior. Use proxy_cookie_flags (available in Nginx 1.19.3+) to append attributes to all upstream cookies:
server {
listen 443 ssl http2;
server_name app.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_cookie_flags session_id HttpOnly Secure SameSite=Strict;
proxy_cookie_path / /;
}
} If you're running older Nginx versions, use proxy_cookie_path with string manipulation or upgrade. For comprehensive server hardening beyond cookies, review the Ubuntu security hardening guide to lock down the entire stack.
Express.js with express-session
const session = require('express-session');
app.use(session({
name: 'session_id',
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
cookie: {
httpOnly: true,
secure: true, // Requires HTTPS; set false only in local dev
sameSite: 'strict', // 'lax' or 'none' as needed
maxAge: 3600000, // 1 hour in milliseconds
path: '/'
}
})); Critical note: Express respects the secure: true flag only when it detects a TLS connection. Behind a reverse proxy, add app.set('trust proxy', 1); so Express reads X-Forwarded-Proto correctly. Without this, cookies won't be sent over HTTPS even though the connection is secure.
Laravel 11+
Laravel centralizes session cookie config in config/session.php:
'cookie' => env('SESSION_COOKIE', 'laravel_session'),
'secure' => env('SESSION_SECURE_COOKIE', true),
'http_only' => true,
'same_site' => 'lax', // 'strict', 'lax', or 'none' Set SESSION_SECURE_COOKIE=true in your .env file for production. Laravel's encryption adds another layer, but don't rely on it as a substitute for proper flags — encrypted cookies leaked via XSS can still be replayed by attackers who capture them before decryption occurs. For database-backed sessions, see the Laravel performance optimization guide to avoid session store bottlenecks under load.
How do you verify and test secure session cookie configuration in production?
Configuration mistakes are silent. You won't get errors — you'll get breaches. Establish verification as part of your deployment pipeline and regular audit cadence.
Manual Verification Checklist
- Inspect raw headers: Run
curl -v https://app.example.com/login -d '...' 2>&1 | grep -i set-cookieand confirm all three flags appear exactly once per session cookie. - Test HttpOnly enforcement: Open browser console on an authenticated page and run
document.cookie. The session cookie must not appear in the output. - Validate Secure flag: Attempt to access the app over plain HTTP (if still available). The session cookie should not be transmitted; ideally, redirect to HTTPS immediately.
- Confirm SameSite behavior: Create a test page on a different domain containing a form that POSTs to your app. Submit it while authenticated. With
StrictorLax, the session cookie must not accompany the request. - Check mobile and legacy browsers: Test on iOS Safari and older Android Chrome. Some legacy browsers ignore
SameSiteentirely; ensure your CSRF token fallback works for these cases.
Automated Testing in CI/CD
Integrate cookie header validation into your deployment pipeline. A simple test script can parse response headers and fail builds that miss required attributes:
#!/bin/bash
RESPONSE=$(curl -sI https://staging.app.example.com/auth/login \
-d "[email protected]&password=secret" \
-c -)
if ! echo "$RESPONSE" | grep -qi "httponly"; then
echo "FAIL: HttpOnly flag missing" >&2
exit 1
fi
if ! echo "$RESPONSE" | grep -qi "secure"; then
echo "FAIL: Secure flag missing" >&2
exit 1
fi
if ! echo "$RESPONSE" | grep -qi "samesite="; then
echo "FAIL: SameSite attribute missing" >&2
exit 1
fi
echo "PASS: All session cookie flags present" This catches regressions before they reach production. Pair this with dependency scanning to catch libraries that might override your cookie settings. Teams practicing DevSecOps should integrate such checks early; the DevSecOps shift-left guide covers broader automation strategies for security controls.
Harden Your Sessions Before the Next Audit
Implementing secure session cookies with SameSite and HttpOnly takes minutes but prevents entire categories of session-based attacks that dominate breach reports year after year. Don't wait for a penetration test finding or compliance auditor to flag this gap. Update your Nginx configs, framework session settings, and CI validation scripts today. Verify every environment — staging often drifts from production, and that's where attackers probe first. If your team needs help auditing session security across complex multi-service architectures or preparing for SOC 2 evidence collection, reach out to discuss your specific setup.