Working with HTTP Cookies in Java Servlets

HTTP cookies provide a mechanism for web servers to store small fragments of stateful data on the client side. During an initial response, the server instructs the browser to store specific key-value pairs. On subsequent requests to the same domain, the browser automatically attaches these cookies to the HTTP headers, enabling session tracking and user preference retention without server-side storage overhead.

Core Characteristics

  • Data is stored as plain text strings using a key-value format.
  • Individual cookie payload is capped at approximately 4096 bytes.
  • Cookies are strictly bound to their originating domain. Cross-domain access between unrelated primary domains is blocked, but sharing across subdomains is supported.
  • Storage location depends on configuration: session cookies reside in browser memory, while persistent cookies are written to the local file system.
  • Browsers enforce a per-domain quota (e.g., Chrome allows up to 50 cookies per host).
  • Every HTTP request to a matching domain automatically includes all relevant cookies in the header.

Creating and Dispatching Cookies

Cookies are instantiated using the Cookie constructor and attached to the HTTP response via the HttpServletResponse API.

Cookie userPreference = new Cookie("theme", "dark");
response.addCookie(userPreference);

Retrieving Client-Side Data

Incoming cookies are extracted from the HttpServletRequest object. The method returns an array that requires null-safe handling.

Cookie[] incomingCookies = request.getCookies();
if (incomingCookies != null) {
    for (Cookie c : incomingCookies) {
        String key = c.getName();
        String val = c.getValue();
        // Process data
    }
}

Handling Non-ASCII Characters

Legacy servlet specifications throw IllegalArgumentException when cookie values contain control characters or raw Unicode. Although modern containers (Servlet 4.0+) tolerate UTF-8, explicitly encoding values remains a robust practice for compatibility and safety.

String rawValue = "用户偏好";
String encoded = URLEncoder.encode(rawValue, StandardCharsets.UTF_8.name());
Cookie dataCookie = new Cookie("pref", encoded);
// Decoding on retrieval:
String decoded = URLDecoder.decode(dataCookie.getValue(), StandardCharsets.UTF_8.name());

Session vs. Persistent Lifecyclse

By default, a newly created cookie operates as a session cookie. It remains active only while the browser process runs and is discarded upon exit. To persist data across browser restarts, assign an explicit expiration window using setMaxAge(int seconds). Once the threshold is reached, the browser automatically purges the file.

Cookie trackingToken = new Cookie("tracker", "abc123");
trackingToken.setMaxAge(3600); // Persists for one hour
response.addCookie(trackingToken);

Domain Scope and Subdomain Routing

Domain hierarchy dictates cookie visibility. A primary domain (e.g., example.com) ranks higher than its subdomains (e.g., api.example.com). Cookies set on a primary domain can be configured to be accessible by subdomains, but cookies cannot be shared across entirely different primary domains due to security isolation policies.

Implementation Examples

Dispatching Cookies with Encoding and Expiration

package dev.webapp.handlers;

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;

@WebServlet("/assign-state")
public class CookieWriterServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        // Session-scoped cookie
        Cookie sessionFlag = new Cookie("visit_type", "first_time");

        // Persistent cookie with encoded non-ASCII value
        String locationData = "北京市";
        String safeValue = URLEncoder.encode(locationData, StandardCharsets.UTF_8.name());
        Cookie locationCookie = new Cookie("user_region", safeValue);
        locationCookie.setMaxAge(1800); // 30 minutes persistence
        locationCookie.setPath("/");

        resp.addCookie(sessionFlag);
        resp.addCookie(locationCookie);
        resp.getWriter().write("Cookies dispatched successfully.");
    }
}

Extracting and Decoding Incoming Cookies

package dev.webapp.handlers;

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Optional;

@WebServlet("/read-state")
public class CookieReaderServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        Cookie[] clientCookies = req.getCookies();
        if (clientCookies == null || clientCookies.length == 0) {
            resp.getWriter().write("No cookies found.");
            return;
        }

        Optional<Cookie> regionCookie = Arrays.stream(clientCookies)
                .filter(c -> "user_region".equals(c.getName()))
                .findFirst();

        if (regionCookie.isPresent()) {
            String raw = regionCookie.get().getValue();
            String decodedRegion = URLDecoder.decode(raw, StandardCharsets.UTF_8.name());
            resp.getWriter().write("Decoded region: " + decodedRegion);
        } else {
            resp.getWriter().write("Region cookie not present.");
        }
    }
}

Security and Storage Guidelines

Cookie payloads are transmitted and stored in plaintext, making them unsuitable for sensitive information such as passwords, tokens, or personal identifiers. Always apply URL encoding to values to prevent header injection and parsing errors. For enhanced security, restrict cookie scope using setPath() and setDomain(), and consider marking sensitive cookies as HttpOnly and Secure to mitigate XSS and network interception risks.

Tags: java servlet HTTP Cookies web development Backend Engineering

Posted on Wed, 28 Oct 2026 16:33:02 +0000 by kim