Cookies are a convenient tool for web apps to remember user data like sessions and preferences. But they also expose major security risks if configured incorrectly! Adding two simple flags – HTTPOnly and Secure – when setting cookies can prevent common attacks like cross-site scripting and session hijacking.
In my experience as a security engineer, improper cookie handling is still a widespread issue I regularly encounter when auditing web apps. In this comprehensive, 2000+ word guide, you‘ll learn how to leverage Nginx to lazily enforce the HTTPOnly and Secure cookie attributes for all your web apps. Let‘s dive in!
The Risks of Unsecured Cookies
Before jumping into the Nginx configuration, it‘s important to understand exactly why HTTPOnly and Secure cookies matter for security.
Cookies were never originally designed with security in mind. They are simply a convenient mechanism for apps to store data in the user‘s browser across requests. But this capability can be hijacked by attackers to devastating effect if cookies are not properly configured.
Some major risks of unsecured cookies include:
Cross-Site Scripting (XSS):
XSS attacks allow adversaries to inject malicious client-side JavaScript into web pages viewed by other users. This malicious code can then access cookies set by the app and send them back to the attacker.
According to research by PreciseSecurity, over 1.5 million XSS attacks occur each month as of 2022. Well-known sites like Facebook, Twitter, and PayPal have all fallen victim to XSS vulnerabilities enabling cookie theft in the past.
The HTTPOnly cookie flag thwarts XSS by blocking client-side JavaScript from reading cookie values. This contains the damage from any successful injection flaws.
Session Hijacking:
Many web apps use cookies to store session IDs that identify a user‘s login state. If stolen, these session IDs allow malicious actors to impersonate victims and access their accounts.
The 2022 Threat Report by F5 Labs found a 29% increase in cookie-based session hijacking attacks. The OWASP Top 10 also lists broken session management as the #3 most critical web app security risk.
The Secure flag mitigates session hijacking by ensuring the browser only sends session ID cookies over encrypted HTTPS connections, never plaintext HTTP.
Man-in-the-Middle Attacks:
On public Wi-Fi networks, attackers can intercept all unencrypted HTTP traffic, viewing and modifying data in transit. This allows interception of login credentials, session IDs, and other sensitive cookie data.
The Secure cookie flag prevents man-in-the-middle attacks by enforcing encryption when transmitting cookie values, keeping them obscured from network snooping.
While the risks are clear, my experience shows developers frequently neglect setting cookie flags due to laziness or ignorance. Let‘s change that!
Cookie Syntax 101
Before diving into the Nginx configs, a quick primer on how cookies work under the hood…
Cookies consist of a name/value pair along with optional attributes like Path, Domain, Expires, Secure, and HTTPOnly. Here is an example cookie header in an HTTP response:
Set-Cookie: sessionID=293nx9sAPx; Expires=Wed, 21 Oct 2022 07:28:00 GMT; Path=/; Domain=.example.com; Secure; HttpOnly
The name of this cookie is "sessionID". Its value is a unique identifier string that the app assigns to track login state. The other parameters tell the browser how to handle and restrict this cookie.
When the browser sees this Set-Cookie header, it will store the cookie and send it back in subsequent requests to the same domain. The web application can then read the sessionID value to look up the user‘s logged in state.
Pretty simple right? But as seen above, failure to enable Secure and HTTPOnly turns this mechanism against us. Let‘s lock it down!
Overview of Nginx Architecture
Nginx is one of the most widely used web servers thanks to its performance and efficiency. Its modular architecture makes it highly customizable and flexible.
Here are some key components relevant to cookie configuration:
nginx.conf – The main Nginx config file where global settings and modules are loaded. Includes other conf files.
http block – The section in nginx.conf defining the HTTP server configuration.
ssl.conf – Optional file where SSL/TLS settings are specified. Gets included in http block.
Module directives – Nginx relies on modules that provide specific functionality like cookie handling.
Knowing where cookies are configured in the Nginx architecture will make our secure cookie implementation smooth sailing. Now let‘s get into it!
Method #1: Using the add_header Directive
The easiest way to set the HTTPOnly and Secure cookie flags is by using the add_header directive from the headers module. Here‘s how:
- Open nginx.conf:
sudo vim /etc/nginx/nginx.conf
- Find the HTTP server block and add:
add_header Set-Cookie "HttpOnly; Secure";
- Save changes and reload Nginx:
sudo nginx -s reload
That‘s all it takes! The add_header line will automatically append the HTTPOnly and Secure flags to any Set-Cookie headers in the response.
For example, if your application sets a cookie like:
Set-Cookie: sessionID=293nx9sAPx
Nginx will modify it to be:
Set-Cookie: sessionID=293nx9sAPx; HttpOnly; Secure
The add_header directive executes on every HTTP response, so you easily implement security in one place for all your web apps.
add_header Directive Usage and Syntax
The add_header directive comes from the headers module and has a few variations in syntax:
# Set a simple response header
add_header Name Value;
# Set multiple headers
add_header Name1 Value1;
add_header Name2 Value2;
# Conditionally set header on certain status codes
add_header Name Value if (condition);
When setting cookie attributes specifically, the value uses the cookie parameter syntax:
Set-Cookie: name=value; HttpOnly; Secure;
The parameters we care about are HttpOnly and Secure. Other common cookie settings like Path, Expires, Domain can be omitted since those are app-specific.
With add_header, you can easily add any global security headers like:
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
This makes add_header a powerful tool for centralizing your HTTP security policies.
Testing HTTPOnly and Secure Cookie Flags
Verifying the cookie flags requires inspecting the raw HTTP headers.
In Chrome/Firefox developers tools, go to the Network tab and examine the Cookie header on any request. You should see:
Cookie: sessionID=xxx; HttpOnly; Secure
You can also use online tools like Web-sniffer.net. Paste in the URL and it will analyze the HTTP headers for you:

The browser may omit the Secure flag on localhost or HTTP connections so test with live HTTPS sites.
Troubleshooting tips if flags are missing:
- Reload Nginx config after making changes
- Check for syntax errors in nginx.conf
- Try a different browser or incognito mode
- Explicitly set cookie in app code as fallback
Going Beyond HTTPOnly and Secure
Some additional cookie security best practices:
Encrypt and hash session IDs – Prevent session prediction by hashing and encrypting ID values on the backend.
Set the cookie Path – Restrict cookie to its necessary subdirectory vs. root.
Short session expiration – Set cookies to expire after X minutes of inactivity. Require reauthentication.
Cookie rotation – Periodically issue new session IDs and expire the old.
Cookie signing – Digitally sign cookie values to prevent tampering.
SameSite cookies – Only send cookies on requests originating from site itself.
These measures will synergize with HttpOnly and Secure to further lock down your cookies!
Method #2: Using proxy_cookie_path
An alternative Nginx directive for setting cookie flags globally is proxy_cookie_path. Here‘s how it‘s used:
- Edit the Nginx SSL config file, usually ssl.conf:
sudo vim /etc/nginx/ssl.conf
- Add the following line:
proxy_cookie_path / "/; HttpOnly; Secure";
- Reload Nginx:
sudo nginx -s reload
Now any cookies set for the root path "/" will inherit the HTTPOnly and Secure flags.
How the proxy_cookie_path Directive Works
The proxy_cookie_path syntax is:
proxy_cookie_path path replacement;
It will match cookies whose Path matches "path" and append "replacement".
For example with our config:
Cookie: name=value; Path=/
Gets converted to:
Cookie: name=value; Path=/; HttpOnly; Secure
The proxy_cookie_path approach differs from add_header in two key ways:
- It is a global configuration, not per-request.
- It only matches cookies with a Path prefix. Cookies with no Path will be skipped.
In general, add_header is preferable for its flexibility and simplicity. But proxy_cookie_path allows modifying Path during replacement.
Conclusion
That wraps up our deep dive on implementing secure HTTPOnly and Secure cookies in Nginx!
We covered the risks of insecure cookies like XSS and session hijacking and why HTTPOnly and Secure flags effectively mitigate these attacks.
For the implementation, we detailed two straightforward approaches:
-
add_header directive – Easily inject cookie flags in all HTTP responses
-
proxy_cookie_path – Alternative method to globally append flags based on Path
Additional tips like testing methodology, troubleshooting, and defense-in-depth strategies will help you operate these cookie configs confidently.
I see insecure cookie usage far too often in my security work. I hope this article has impressed upon you the importance of properly setting the HTTPOnly and Secure flags. Implementing that simple change will massively frustrate attackers and thwart common exploits!
Let me know if you have any other questions about securing cookies or hardening Nginx. I‘m always happy to chat more about web app security!