Introduction

Cookies are the cornerstone of state management on the web. They enable everything from user authentication to personalization, but their ubiquity also makes them a prime target for attackers. One of the most effective, yet often misunderstood, defenses against client‑side attacks is the HttpOnly flag. When correctly applied, HttpOnly can dramatically reduce the risk of session hijacking via cross‑site scripting (XSS) and other client‑side exploits.

In this article we will:

  • Explain what the HttpOnly attribute is and how browsers enforce it.
  • Explore the threat model it addresses and the attacks it mitigates.
  • Show how to set HttpOnly in popular server‑side languages and frameworks.
  • Compare HttpOnly with related cookie attributes such as Secure and SameSite.
  • Provide practical testing, debugging, and monitoring strategies.
  • Discuss common pitfalls, limitations, and future directions.

By the end, you’ll have a solid understanding of when and how to use HttpOnly, and you’ll be equipped to audit existing applications for proper cookie hygiene.


Table of Contents

  1. What Is the HttpOnly Flag?
  2. Why HttpOnly Matters: Threat Landscape
    1. Cross‑Site Scripting (XSS)
    2. Session Hijacking
  3. How Browsers Enforce HttpOnly
  4. Setting HttpOnly in Different Environments
    1. Node.js / Express
    2. PHP
    3. Java / Spring Boot
    4. Python / Django & Flask
    5. ASP.NET Core
  5. HttpOnly vs. Secure vs. SameSite
  6. Testing and Verifying HttpOnly Cookies
    1. Browser Developer Tools
    2. Automated Scanners
  7. Common Pitfalls and Misconfigurations
  8. Advanced Topics
    1. HttpOnly with Sub‑Domain Cookies
    2. Cookie Partitioning & First‑Party Isolation
    3. Future of HttpOnly: Emerging Standards
  9. Best‑Practice Checklist
  10. Conclusion
  11. Resources

What Is the HttpOnly Flag?

The HttpOnly attribute is an optional flag that can be attached to a cookie when it is sent from a server to a browser via the Set-Cookie HTTP response header. Its syntax is simple:

Set-Cookie: sessionId=abc123; HttpOnly; Path=/; Secure; SameSite=Strict

When the HttpOnly flag is present, browsers must not expose the cookie to client‑side scripts (i.e., JavaScript’s document.cookie API). The cookie remains accessible to the browser’s HTTP stack, meaning it will be automatically included in subsequent HTTP requests that match the cookie’s scope (domain, path, and secure attributes).

Key points:

  • Server‑only access – Only the server can read/write the cookie via HTTP headers.
  • No JavaScript read/writedocument.cookie will not list HttpOnly cookies, nor can scripts set them.
  • Mitigation, not a silver bullet – HttpOnly reduces risk but does not replace proper input validation, CSP, or other defensive layers.

Why HttpOnly Matters: Threat Landscape

Cross‑Site Scripting (XSS)

XSS occurs when an attacker injects malicious JavaScript into a vulnerable web page. Once executed, the script runs with the same privileges as the legitimate page, gaining access to everything the page can see—including cookies via document.cookie. If a session cookie is exposed, the attacker can hijack the user’s session.

HttpOnly blocks the most common XSS exploitation path: stealing the session identifier from the client. While sophisticated XSS attacks can still perform actions on behalf of the user (e.g., CSRF via forged requests), they can no longer simply exfiltrate the cookie.

Quote: “HttpOnly is a critical line of defense that turns a client‑side script from a secret‑stealing tool into a mere browser automation script.” – OWASP

Session Hijacking

Session hijacking is the broader category of attacks that aim to take over a user’s authenticated session. With an HttpOnly cookie, an attacker cannot directly read the session token, but they may still:

  • Perform session fixation – If the server accepts arbitrary session IDs.
  • Exploit other client‑side vulnerabilities – Such as DOM‑based XSS that manipulates the page without needing the cookie value.

Thus, HttpOnly is a defense‑in‑depth measure, not a stand‑alone solution.


How Browsers Enforce HttpOnly

When a browser receives a Set-Cookie header containing the HttpOnly attribute, it stores the cookie in its internal cookie jar. The enforcement rules are:

ActionAllowed?Reason
Include cookie in an HTTP request that matches domain/pathPart of the HTTP protocol
Access cookie via document.cookie or any JavaScript APIFlag instructs the browser to hide it from script contexts
Modify cookie via Set-Cookie response from serverServer-controlled
Delete cookie via Set-Cookie: name=; expires=Thu, 01 Jan 1970 00:00:00 GMTServer-controlled
Read cookie via XMLHttpRequest/fetch response headers❌ (unless server echoes it)Browser does not expose Set-Cookie headers to scripts

All major browsers (Chrome, Edge, Firefox, Safari) honor this flag. However, legacy browsers (e.g., Internet Explorer 6) did not support HttpOnly, which historically forced developers to rely on additional mitigations. Modern browsers have consistent behavior, making HttpOnly a reliable security control.


Setting HttpOnly in Different Environments

Below are practical examples for setting HttpOnly cookies across common server‑side stacks.

Node.js / Express

// app.js (Express 4.x)
const express = require('express');
const app = express();

app.post('/login', (req, res) => {
  // Authenticate user...
  const token = generateSessionToken(req.body.username);
  // Set HttpOnly cookie
  res.cookie('sessionId', token, {
    httpOnly: true,          // <-- HttpOnly flag
    secure: true,            // send only over HTTPS
    sameSite: 'strict',      // optional, see later section
    maxAge: 24 * 60 * 60 * 1000 // 1 day
  });
  res.json({ success: true });
});

app.listen(3000, () => console.log('Server running on :3000'));

Notes:

  • res.cookie is part of the cookie-parser middleware or built‑in Express API.
  • Always combine httpOnly: true with secure: true for production to enforce transport security.

PHP

<?php
// login.php
session_start();

// After successful authentication:
$sessionId = bin2hex(random_bytes(32));

// Set HttpOnly cookie
setcookie(
    'sessionId',
    $sessionId,
    [
        'expires' => time() + 86400, // 1 day
        'path' => '/',
        'domain' => '',              // current domain
        'secure' => true,            // HTTPS only
        'httponly' => true,          // <-- HttpOnly flag
        'samesite' => 'Strict'       // optional
    ]
);
?>

PHP 7.3+ supports the associative array syntax for setcookie. For older versions, you must manually construct the header:

header('Set-Cookie: sessionId=' . $sessionId . '; HttpOnly; Secure; SameSite=Strict; Path=/; Max-Age=86400');

Java / Spring Boot

// LoginController.java
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.*;

@RestController
public class LoginController {

    @PostMapping("/login")
    public String login(@RequestBody LoginRequest request,
                        HttpServletResponse response) {
        // Authenticate user...
        String token = TokenService.createToken(request.getUsername());

        Cookie cookie = new Cookie("SESSIONID", token);
        cookie.setHttpOnly(true);          // <-- HttpOnly
        cookie.setSecure(true);            // HTTPS only
        cookie.setPath("/");
        cookie.setMaxAge(24 * 60 * 60);     // 1 day
        cookie.setSameSite("Strict");       // Spring 6+ (or via response header)

        response.addCookie(cookie);
        return "OK";
    }
}

If you use Spring Security, you can configure the session cookie centrally:

http
    .sessionManagement()
        .sessionFixation().migrateSession()
        .sessionCookie(cookie -> cookie
            .httpOnly(true)
            .secure(true)
            .sameSite("Strict"));

Python / Django & Flask

Django

# settings.py
SESSION_COOKIE_HTTPONLY = True          # Default is True
SESSION_COOKIE_SECURE = True            # Recommended for production
SESSION_COOKIE_SAMESITE = 'Strict'      # Optional

Django automatically adds the HttpOnly attribute to the session cookie if SESSION_COOKIE_HTTPONLY is True. For custom cookies:

def set_custom_cookie(response):
    response.set_cookie(
        key='my_token',
        value=generate_token(),
        httponly=True,       # <-- HttpOnly
        secure=True,
        samesite='Lax',
        max_age=86400
    )

Flask

from flask import Flask, make_response, request
import secrets

app = Flask(__name__)

@app.route('/login', methods=['POST'])
def login():
    # Authenticate user...
    token = secrets.token_urlsafe(32)

    resp = make_response({'status': 'ok'})
    resp.set_cookie(
        'sessionId',
        token,
        httponly=True,          # <-- HttpOnly
        secure=True,            # HTTPS only
        samesite='Strict',
        max_age=24*60*60
    )
    return resp

Flask’s set_cookie API mirrors the underlying Werkzeug implementation.

ASP.NET Core

// Startup.cs (ConfigureServices)
services.ConfigureApplicationCookie(options =>
{
    options.Cookie.HttpOnly = true;      // <-- HttpOnly
    options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
    options.Cookie.SameSite = SameSiteMode.Strict;
});

Or manually in a controller:

public IActionResult Login()
{
    var token = GenerateToken();
    var cookieOptions = new CookieOptions
    {
        HttpOnly = true,
        Secure = true,
        SameSite = SameSiteMode.Strict,
        Expires = DateTimeOffset.UtcNow.AddDays(1)
    };
    Response.Cookies.Append("sessionId", token, cookieOptions);
    return Ok();
}

HttpOnly vs. Secure vs. SameSite

AttributePurposeTypical Use‑CaseBrowser Support
HttpOnlyPrevents client‑side script access to the cookie.Mitigate XSS‑based token theft.All modern browsers (since ~2003).
SecureEnsures cookie is sent only over HTTPS.Prevent eavesdropping on unsecured connections.All browsers; ignored on HTTP.
SameSiteControls cross‑site request handling. Values: Lax, Strict, None.Defend against CSRF; improve privacy.Chrome 51+, Firefox 60+, Edge 79+, Safari 12+.

These attributes are orthogonal; you should generally enable all three for session cookies:

Set-Cookie: sessionId=xyz; HttpOnly; Secure; SameSite=Strict
  • HttpOnly protects against script extraction.
  • Secure protects against network sniffing.
  • SameSite protects against cross‑site request forgery.

Testing and Verifying HttpOnly Cookies

Browser Developer Tools

  1. Open Chrome DevTools (F12) → Application tab → Cookies.
  2. Locate your cookie; the HttpOnly column should read .
  3. Switch to the Console and attempt:
document.cookie   // HttpOnly cookies will NOT appear

If you see the cookie value, the flag is missing.

Automated Scanners

  • OWASP ZAP – Use the “Active Scan” rule “Cookie HttpOnly flag not set”.
  • Burp Suite – In the “Scanner” module, look for the “Missing HttpOnly flag” issue.
  • Qualys WAS – Provides a “Cookie Security” audit.

Running these tools on staging environments helps catch misconfigurations before production.


Common Pitfalls and Misconfigurations

PitfallImpactHow to Fix
Setting HttpOnly on non‑session cookies onlyLeaves session cookie vulnerable.Ensure all authentication‑related cookies have HttpOnly.
Forgetting Secure with HttpOnlyCookie may be sent over HTTP, exposing it to network sniffing.Always combine Secure with HttpOnly for production.
Using SameSite=None without SecureModern browsers reject such cookies, breaking functionality.When SameSite=None is needed (e.g., third‑party auth), also set Secure.
Overriding HttpOnly via JavaScriptSome frameworks expose helper functions that inadvertently clear the flag.Review any client‑side cookie manipulation libraries.
Setting HttpOnly on sub‑domain cookies inconsistentlyAttackers can target less‑protected sub‑domains.Apply consistent policy across all sub‑domains, or use the Domain attribute cautiously.

Advanced Topics

HttpOnly with Sub‑Domain Cookies

When you set a cookie with Domain=.example.com, it is shared across all sub‑domains (app.example.com, api.example.com). If any sub‑domain fails to enforce HttpOnly, an attacker could compromise the whole domain’s session token.

Best practice:

  • Keep a single origin for authentication (e.g., auth.example.com).
  • Issue session cookies with Domain=auth.example.com (no domain attribute) to limit scope.
  • Use SameSite=Lax/Strict to further restrict cross‑site usage.

Modern browsers (Safari’s Intelligent Tracking Prevention, Chrome’s “Partitioned Cookies”) are moving toward first‑party isolation, where cookies set in a third‑party context are stored separately per top‑level site. While this does not directly affect HttpOnly, it changes how cookies are shared across frames and can impact legacy applications that rely on third‑party session cookies.

Developers should:

  • Audit third‑party integrations (e.g., embedded payment widgets).
  • Prefer postMessage or OAuth redirects instead of shared cookies.

Future of HttpOnly: Emerging Standards

  • **HttpOnly is being superseded by the Cookie attribute HttpOnly in the Cookie Store API for Service Workers, enabling explicit control over exposure.
  • Secure and SameSite are evolving with the Cookie header Priority attribute, influencing how browsers treat storage limits.
  • Partitioned Cookies may affect how HttpOnly enforcement is scoped per top‑level site; developers need to stay aware of Chrome’s upcoming changes (expected 2026).

Best‑Practice Checklist

  • Set HttpOnly on every authentication‑related cookie (session, refresh tokens, CSRF tokens).
  • Pair HttpOnly with Secure for all production traffic.
  • Use SameSite=Strict where possible; fallback to Lax for navigation‑based flows.
  • Avoid setting cookies on the root domain unless absolutely necessary.
  • Test with multiple browsers (Chrome, Firefox, Safari, Edge) and mobile equivalents.
  • Run automated scans (OWASP ZAP, Burp) on every CI pipeline.
  • Log cookie attributes (but never log the cookie value) for audit trails.
  • Document cookie policy in your security guidelines and onboarding material.

Conclusion

The HttpOnly flag is a simple, widely supported, and highly effective tool in the web security arsenal. By preventing client‑side scripts from accessing sensitive cookies, it blocks one of the most common avenues for session hijacking via XSS attacks. However, it must be used in concert with Secure, SameSite, and a robust overall security posture that includes input validation, CSP, and regular vulnerability scanning.

Implementing HttpOnly correctly across different platforms is straightforward—most modern frameworks expose a single boolean or configuration option. Yet the real challenge lies in maintaining consistent policies across all services, sub‑domains, and environments, and ensuring that developers and DevOps teams understand the consequences of misconfiguration.

By following the guidelines, examples, and checklist provided in this article, you can confidently harden your web applications against client‑side cookie theft and move one step closer to a secure, privacy‑respecting user experience.


Resources