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.