
Written By
Prabhashana Wijesinghe
Associate Software Engineer
Why Does This Matter in the First Place?
As software developers, we spend a lot of time thinking about how to keep our web applications’ most sensitive data secure while logged, especially data like user authentication tokens. One of the most common mistakes beginner developers make is storing those sensitive data (like JWT tokens) in localStorage or sessionStorage, which can be vulnerable to attacks.
This is where HttpOnly cookies come in handy. In this article, I’ll Walk you through what this special kind of cookie is & why it matters, how it works, how it protects our applications’ sensitive data, how to use it in real-world applications and should we always use these cookies?
What is an HttpOnly Cookie?
Simply it’s a type of cookie with an special attribute that prevents JavaScript from accessing it via document.cookie. So, the key point is that only the server can set or read it through HTTP headers.
How does it work?
Server sets a cookie with the HttpOnly flag(the above mentioned special attribute). Then the browser stores it securely (unreadable by Javascript). Now with every request, the browser sends this cookie automatically to the server.
What does HttpOnly protect against?
I know you may now be curious to learn how HttpOnly actually protects our application, and what it protects against.
Imagine you visit a web application that has a hidden bug. An attacker sneaks in a malicious script like <script>stealCookies()</script> into the page and this is called XSS (Cross-Site Scripting). Normally, that script could run in your browser and try to read your cookies using document.cookie. So, if your login token was stored in a normal cookie or in localStorage, the attacker could grab it easily and pretend to be you.

But since you now know about HttpOnly cookies, if the above web application had used an HttpOnly cookie instead of a normal cookie, penetration would not have been possible. Because,
- The cookie is invisible to JavaScript.
- Even if the attacker’s script runs, it can’t see or steal the token.
- Your login session stays safe.

Sample Code examples
Now, let’s look at some sample code to get an idea of how to use HttpOnly cookies in your backend and frontend applications. Here, I’ve chosen Java SpringBoot for the backend controller and Angular for the frontend service.
@RestController
@RequestMapping("/auth")
public class AuthController {
@PostMapping("/login")
public ResponseEntity login(@RequestBody LoginRequest loginRequest, HttpServletResponse response) {
// Step 1: Check if the username & password are correct. In the real world, this would be a database lookup.
if ("admin".equals(loginRequest.getUsername()) && "password".equals(loginRequest.getPassword())) {
// Step 2: Generate a token to represent the user's session. In real apps, developer would create a JWT or a random session ID.
String token "sample-jwt-token-123";
// Step 3: Create a cookie that will store this token.
ResponseCookie cookie ResponseCookie.from("sessionId", token)
.httpOnly(true) // Here we have used HttpOnly flag where it hides cookie from JavaScript (protects against XSS)
.secure(true) // Here sending cookie only over HTTPS, never over HTTP
.sameSite("Strict")// Blocks most CSRF attacks (cookie only sent from same site)
.path("/") // Cookie is valid for the entire application (not just /auth)
.maxAge (6060) // Here we have set cookie to expire after 1 hour
.build();
// Step 4: Add the cookie to the response headers.
response.addHeader("Set-Cookie", cookie.toString());
// Step 5: Tell the frontend that login was successful.
return ResponseEntity.ok("Login successful");
}
// If username/password are wrong, reject the login.
return ResponseEntity.status (HttpStatus.UNAUTHORIZED).body("Invalid credentials");
}
}
So, what above spring controller does is when a user logs in with the right credentials,
- In the controller we created a fake token (in real apps, this would be a JWT or session ID).
- Then we put that token inside a cookie with special security rules:
- HttpOnly → JavaScript can’t steal it.
- Secure → Only sent over HTTPS.
- SameSite → Helps protect against CSRF.
The browser automatically saves this cookie and sends it back with every request, so the server knows the user is logged in.
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
@Injectable({ providedIn: 'root' })
export class AuthService {
constructor(private http: HttpClient) {}
login(username: string, password: string) {
return this.http.post(
'http://localhost:8080/auth/login',
{ username, password },
{ withCredentials: true } // This allows cookies to be sent
);
}
getUserData() {
return this.http.get(
'http://localhost:8080/user/me',
{ withCredentials: true } // Cookie will be attached automatically
);
}
}
this.authService.login(this.username, this.password).subscribe({
next: () => console.log("Login successful"),
error: () => console.error("Login failed")
});
As you can see in above code, on the frontend side, we’re using Angular’s HttpClient to talk to the backend
- When the user logs in, we send their credentials to /auth/login.
- Then the { withCredentials: true } option tells the browser: “please include cookies with this request.”
- Once the backend responds with a cookie, the browser saves it automatically.
Now in every future request (like getUserData()), the cookie is sent in request headers automatically, so the server knows who the user is.
Now below is the sample request header when user logging to the application
Set-Cookie: sessionId=sample-jwt-token-123; HttpOnly; Secure; SameSite Strict; Path=/; Max-Age=3600
When not to use HttpOnly ?
Now I know you’re probably thinking “If HttpOnly cookies are more secure, why don’t we just use them for every other cookie use case instead of normal cookies?”. But there are some valid cases where you need to let the JavaScript read the cookie, and using only HttpOnly would break their functionality. So, following are a few example use cases to get an idea why you can’t use HttpOnly cookies all the time.
User Preferences:
- Dark mode toggle (theme=dark), language settings, layout options.
- Your frontend needs to read these values to apply UI changes. So, if you made them HttpOnly, JS can’t read them, so the feature won’t work.
JS-dependent Tracking/Analytics Cookies:
- Tools like Google Analytics rely on JavaScript reading cookies. So, if we set them HttpOnly, tracking scripts won’t work.
Conclusion
HttpOnly cookies are a simple but powerful tool to make our web applications more secure. They help protect sensitive data, especially authentication tokens, by keeping them out of reach of JavaScript, which means attackers can’t steal them via common threats like XSS attacks.
However, as we’ve seen, HttpOnly cookies are not a one-size-fits-all solution. So, use them for server-only secrets like login sessions or refresh tokens, but avoid using them for values your frontend needs to read directly such as user preferences, themes, or JavaScript-dependent tracking.
By understanding when and how to use HttpOnly cookies, you can build applications that are both secure and functional, giving your users a safer and smoother experience.
Key Takeaways
- HttpOnly cookies hide sensitive data from JavaScript: attackers can’t steal authentication tokens via XSS.
- Use them for secrets only: session IDs, JWT tokens, or refresh tokens that only the server needs.
- Don’t use them for everything: frontend-readable data like themes, language settings, or JS-dependent analytics need normal cookies.
- Combine with Secure and SameSite flags: this ensures cookies are sent safely over HTTPS and reduces CSRF risks.
- Browser handles them automatically: once set, the cookie is sent with each request without needing extra code on the frontend.