yum upgrades for production use, this is the repository for you.
Active subscription is required.
Your web application might set all the right cookies, but if those cookies lack HttpOnly, Secure, and SameSite flags, attackers can steal session tokens through cross-site scripting (XSS) or intercept them over unencrypted connections. The NGINX cookie flag module solves this problem at the reverse proxy layer, enforcing cookie security flags without modifying application code.
In this guide, you will learn how to install and configure the set_cookie_flag directive in NGINX. This directive automatically adds security attributes to every Set-Cookie header that passes through your server, including full SameSite=None support for cross-site cookies.
Why Use the NGINX Cookie Flag Module
Adding cookie security flags in NGINX offers several advantages over modifying each application individually:
- Centralized enforcement. A single NGINX configuration secures cookies from all backends — PHP-FPM, Node.js, Python, Java, and any other upstream. You do not need to touch application code.
- Legacy application support. Many older frameworks and third-party backends do not set
HttpOnly,Secure, orSameSiteon their cookies. The NGINX cookie flag module fills that gap without requiring application upgrades. - Universal cookie coverage. Unlike the built-in
proxy_cookie_flagsdirective, this module works with all cookie sources: reverse proxy backends, FastCGI (PHP-FPM), uwsgi, SCGI, and even cookies set by NGINX modules likeuserid. - Defense in depth. Even if an application developer forgets to set cookie flags, NGINX catches the omission. This provides an additional security layer that operates independently of application logic.
- Simple configuration. A single
set_cookie_flagdirective can secure all cookies with a wildcard, or you can define granular per-cookie policies.
How the NGINX Cookie Flag Module Works
The cookie flag module operates as an output header filter. It inserts itself into the NGINX header filter chain and inspects every outgoing Set-Cookie header before it reaches the client.
Here is what happens during each request:
- The upstream backend (or NGINX itself) generates a response with one or more
Set-Cookieheaders. - The cookie flag filter examines each
Set-Cookieheader against your configured rules. - For each matching cookie, the module appends the specified flags (
HttpOnly,Secure,SameSite) only if they are not already present. - The modified headers are sent to the client.
Because the module operates as a header filter rather than a proxy-specific directive, it works with all cookie sources: reverse proxy backends, FastCGI (PHP-FPM), uwsgi, SCGI, and cookies set by NGINX modules. This is a key advantage over the built-in proxy_cookie_flags directive, which only modifies cookies from proxy_pass backends.
The module is also idempotent. If a backend already sets HttpOnly on a cookie, the module will not duplicate the flag. This means you can safely apply it to any backend without worrying about double flags in your Set-Cookie headers.
Installation
The NGINX cookie flag module is available as a prebuilt dynamic module from the GetPageSpeed repository.
RHEL, CentOS, AlmaLinux, Rocky Linux 8+
sudo dnf install https://extras.getpagespeed.com/release-latest.rpm
sudo dnf install nginx-module-cookie-flag
Then add the following line at the top of /etc/nginx/nginx.conf, before any http, events, or other blocks:
load_module modules/ngx_http_cookie_flag_filter_module.so;
RHEL 7, Amazon Linux 2
sudo yum install https://extras.getpagespeed.com/release-latest.rpm
sudo yum install nginx-module-cookie-flag
Then add the load_module directive to /etc/nginx/nginx.conf as shown above.
Debian, Ubuntu
sudo apt-get update
sudo apt-get install nginx-module-cookie-flag
On Debian and Ubuntu, the module is automatically enabled via a drop-in configuration file. You do not need to add the load_module directive manually.
After installation on any platform, verify that NGINX accepts the configuration:
sudo nginx -t
Configuration Reference
The module provides a single directive: set_cookie_flag.
Syntax
set_cookie_flag <cookie_name|*> [HttpOnly] [secure] [SameSite|SameSite=Lax|SameSite=Strict|SameSite=None];
Context
server, location
Note: The directive is not available in the http block. You must place it inside a server or location block.
Parameters
The first parameter is the cookie name or * (wildcard). Subsequent parameters are the flags to apply. You must specify at least one flag and can specify up to three.
Supported flags:
| Flag | Effect |
|---|---|
HttpOnly |
Prevents JavaScript access to the cookie via document.cookie |
secure |
Restricts the cookie to HTTPS connections only |
SameSite |
Sets bare SameSite attribute (equivalent to Strict in most browsers) |
SameSite=Lax |
Allows the cookie in top-level navigations but blocks it in cross-site subrequests |
SameSite=Strict |
Blocks the cookie in all cross-site contexts |
SameSite=None |
Cookie sent on all cross-site requests (requires secure flag) |
Flag names are case-insensitive. Writing httponly, HttpOnly, or HTTPONLY all produce the same result.
Configuration Examples
Secure All Cookies with a Wildcard
The simplest and most common configuration applies all three security flags to every cookie:
server {
listen 443 ssl;
server_name example.com;
location / {
proxy_pass http://backend;
set_cookie_flag * HttpOnly secure SameSite=Lax;
}
}
This adds HttpOnly, Secure, and SameSite=Lax to every Set-Cookie header in responses from the / location. Any cookie that already has one of these flags will not receive a duplicate.
Per-Cookie Policies
You can target specific cookies by name and apply different flags to each. Use the wildcard * as a fallback for cookies without explicit rules:
location / {
proxy_pass http://backend;
set_cookie_flag PHPSESSID HttpOnly secure SameSite=Strict;
set_cookie_flag JSESSIONID HttpOnly secure SameSite=Strict;
set_cookie_flag csrf_token HttpOnly secure SameSite=Lax;
set_cookie_flag * HttpOnly;
}
In this configuration, PHPSESSID and JSESSIONID get the full set of security flags with SameSite=Strict. The csrf_token uses SameSite=Lax because CSRF tokens must survive top-level form submissions from external sites. All other cookies receive only HttpOnly.
Important: When a named rule matches a cookie, the wildcard rule is skipped entirely for that cookie. The wildcard is not additive. If you want PHPSESSID to have HttpOnly, Secure, and SameSite=Strict, you must list all three flags in the named rule.
SameSite=None for Cross-Site Cookies
Since Chrome 80, cookies that need to be sent in cross-site contexts (such as embedded iframes, third-party widgets, OAuth flows, and advertising pixels) must be marked with SameSite=None and the secure flag. The NGINX cookie flag module fully supports this requirement:
location / {
proxy_pass http://backend;
set_cookie_flag TrackingID SameSite=None secure;
set_cookie_flag OAuthState SameSite=None secure;
set_cookie_flag * HttpOnly secure SameSite=Lax;
}
This configuration marks TrackingID and OAuthState as cross-site cookies while applying a stricter SameSite=Lax default to everything else. Without SameSite=None, browsers like Chrome, Firefox, and Edge will block these cookies in cross-site requests, breaking embedded content and third-party integrations.
Server-Level Defaults with Location Overrides
You can define a default policy at the server level and override it for specific locations:
server {
listen 443 ssl;
server_name example.com;
set_cookie_flag * HttpOnly secure SameSite=Lax;
location / {
proxy_pass http://backend;
}
location /api {
proxy_pass http://api-backend;
set_cookie_flag * HttpOnly secure SameSite=Strict;
}
location /embed {
proxy_pass http://widget-backend;
set_cookie_flag WidgetSession SameSite=None secure;
set_cookie_flag * HttpOnly secure SameSite=Lax;
}
}
The /api location uses SameSite=Strict because API endpoints typically do not need cross-site cookie access. The /embed location marks the WidgetSession cookie with SameSite=None for cross-site iframe embedding. The rest of the site uses SameSite=Lax.
Configuration inheritance works on an all-or-nothing basis. If a child location defines any set_cookie_flag directive, it completely replaces the parent configuration. There is no merging of individual cookie rules between parent and child.
Production-Grade Quality
The NGINX cookie flag module from GetPageSpeed is built for production environments with a focus on reliability and correctness.
Full SameSite=None Support
Since Chrome 80 (released February 2020), browsers require SameSite=None for cookies that must be sent in cross-site contexts. This module fully supports SameSite=None, making it compatible with modern browser requirements for OAuth flows, embedded iframes, advertising pixels, and third-party integrations.
Memory-Safe Implementation
The module uses NGINX-native memory APIs throughout. All string operations use safe NGINX functions like ngx_strlchr and ngx_snprintf instead of C standard library functions that assume null-terminated strings. NGINX strings (ngx_str_t) are length-delimited, not null-terminated, so using standard C functions like strchr or strcpy on them can read past buffer boundaries. This module handles NGINX strings correctly.
Duplicate Flag Detection
The module validates your configuration at startup. If you accidentally specify conflicting SameSite values for the same cookie, NGINX will refuse to start and report a clear error:
nginx: [emerg] Duplicate flag "SameSite" (SameSite=Strict) detected
This catches configuration mistakes before they reach production, rather than silently applying unpredictable behavior.
Idempotent Flag Application
The module checks each Set-Cookie header for existing flags before appending new ones. If a backend already sets HttpOnly on a cookie, the module will not add a second HttpOnly flag. This prevents malformed headers and ensures clean Set-Cookie values regardless of what the upstream sends.
Comprehensive Test Suite
The module includes a full test suite that verifies every flag combination, wildcard matching, idempotent behavior, per-cookie rules, and error handling. Automated CI/CD testing runs against both NGINX stable and mainline branches to ensure compatibility with every release.
Comparison with proxy_cookie_flags
NGINX 1.19.3 introduced the built-in proxy_cookie_flags directive. Both approaches set cookie security flags, but they differ in important ways:
| Feature | set_cookie_flag (this module) |
proxy_cookie_flags (built-in) |
|---|---|---|
| Cookie sources | All (proxy, FastCGI, uwsgi, SCGI, NGINX modules) | Proxy only (proxy_pass) |
| Cookie matching | Exact name or * wildcard |
Exact name or ~ regex |
SameSite=None support |
Yes | Yes |
| Variable support | No | Yes (since NGINX 1.19.8) |
| Available contexts | server, location |
http, server, location |
| Requires module install | Yes | No (built-in since 1.19.3) |
| Flag removal | No | Yes (nosecure, nohttponly, nosamesite) |
| Duplicate detection | Yes (rejects conflicting SameSite at config time) | No |
| Idempotent | Yes (never duplicates existing flags) | No (may duplicate flags) |
When to use each:
- Use
set_cookie_flagif you have FastCGI (PHP-FPM), uwsgi, or SCGI backends, or if cookies originate from NGINX modules likeuserid. Additionally, choose this module when you want idempotent flag application and config-time validation of conflicting SameSite values. - Use
proxy_cookie_flagsif you only useproxy_passbackends, need regex-based cookie matching, need variable support, or need to remove flags from cookies.
For a reverse proxy setup, either approach works. The NGINX cookie flag module is particularly valuable for PHP applications served via FastCGI, where proxy_cookie_flags has no effect.
Testing and Verification
After adding the set_cookie_flag directives, verify the configuration syntax:
sudo nginx -t
Then reload NGINX:
sudo systemctl reload nginx
To confirm that cookie flags are applied, send a request and inspect the response headers:
curl -sI https://example.com/ | grep -i set-cookie
You should see output like:
Set-Cookie: PHPSESSID=abc123; path=/; HttpOnly; secure; SameSite=Strict
Set-Cookie: preferences=dark; path=/; HttpOnly
Verifying Idempotency
If your backend already sets some flags, the module will not duplicate them. For example, if your application sends:
Set-Cookie: token=xyz; Path=/; HttpOnly
And your NGINX configuration has set_cookie_flag * HttpOnly secure SameSite=Lax, the result will be:
Set-Cookie: token=xyz; Path=/; HttpOnly; secure; SameSite=Lax
The HttpOnly flag appears only once because the module checks for its presence before appending.
Verifying SameSite=None
For cross-site cookies, confirm that both SameSite=None and secure appear:
curl -sI https://example.com/embed | grep -i set-cookie
Expected output:
Set-Cookie: WidgetSession=abc; Path=/; secure; SameSite=None
Without the secure flag alongside SameSite=None, browsers will reject the cookie entirely.
You can also use browser developer tools. Open the Network tab, select a request, and check the Response Headers section for Set-Cookie entries with the expected flags. The Application tab in Chrome DevTools shows cookie attributes in a structured table, making it easy to verify each flag.
Troubleshooting
“set_cookie_flag directive is not allowed here”
This error means you placed the directive in the http block. Move it to a server or location block:
# Wrong - http block not supported
http {
set_cookie_flag * HttpOnly; # ERROR
}
# Correct - server block
server {
set_cookie_flag * HttpOnly;
}
“The cookie value has already set in previous directives”
This error occurs when you define set_cookie_flag twice for the same cookie name within the same block:
# Wrong - duplicate cookie name
set_cookie_flag PHPSESSID HttpOnly secure;
set_cookie_flag PHPSESSID SameSite=Lax; # ERROR
Combine all flags into a single directive instead:
# Correct - all flags in one directive
set_cookie_flag PHPSESSID HttpOnly secure SameSite=Lax;
“Duplicate flag SameSite detected”
This error occurs when you specify two different SameSite values for the same cookie:
# Wrong - conflicting SameSite values
set_cookie_flag Secret SameSite=Lax SameSite=Strict; # ERROR
Choose one SameSite value per cookie. You cannot apply both Lax and Strict to the same cookie.
“The parameter value is incorrect”
The module only accepts these exact flag values: HttpOnly, secure, SameSite, SameSite=Lax, SameSite=Strict, and SameSite=None. Any other value triggers this error.
“The number of arguments is incorrect”
You must specify between 1 and 3 flags per directive. Providing zero flags or more than three flags triggers this error:
# Wrong - no flags specified
set_cookie_flag PHPSESSID; # ERROR
# Correct - at least one flag
set_cookie_flag PHPSESSID HttpOnly;
Flags Not Appearing in Response
If cookie flags are not being added, check these common causes:
- Module not loaded: Verify that
load_module modules/ngx_http_cookie_flag_filter_module.so;appears at the top ofnginx.conf(not required on Debian/Ubuntu). - Wrong context: Ensure
set_cookie_flagis inside the correctserverorlocationblock that handles the request. - Location override: If a child location defines its own
set_cookie_flag, it completely replaces the parent configuration. Verify that your target location has the expected rules. - No Set-Cookie headers: The module only modifies existing
Set-Cookieheaders. It does not create new cookies.
Security Best Practices
Apply All Three Flags to Session Cookies
Session cookies should always have HttpOnly, Secure, and a SameSite value. This is the minimum recommended by OWASP:
set_cookie_flag PHPSESSID HttpOnly secure SameSite=Strict;
Use SameSite=Strict for Authentication Cookies
For cookies that manage authentication sessions (such as session IDs, JWT tokens stored in cookies, or TOTP authentication persistence cookies), use SameSite=Strict to prevent them from being sent in any cross-site context.
Use SameSite=Lax for General Cookies
For cookies that need to survive top-level navigations (for example, a user clicking a link to your site from an email), use SameSite=Lax. This balances security with usability.
Use SameSite=None Only When Required
Reserve SameSite=None for cookies that genuinely need cross-site access, such as third-party widgets, OAuth state tokens, or advertising tracking. Always pair SameSite=None with the secure flag — browsers reject SameSite=None cookies without secure.
Always Use HTTPS
The Secure flag only makes sense when your site is served over HTTPS. Combine it with proper security headers including HTTP Strict Transport Security (HSTS) to ensure all traffic uses encrypted connections.
Combine with Defense in Depth
Cookie flags are one layer of defense. Additionally, consider:
- Rate limiting to prevent brute-force attacks
- Bot protection with cookie-based challenges
- Anti-CSRF tokens for forms that modify data
Performance Considerations
The NGINX cookie flag module has minimal performance impact. It operates only during the header filter phase, which runs once per response. The module:
- Iterates only over
Set-Cookieheaders, not all response headers - Performs case-insensitive string comparisons to detect existing flags
- Allocates small amounts of memory from the request pool (freed automatically after the request completes)
For responses without Set-Cookie headers, the overhead is a single null check on the configuration pointer, which is negligible.
Conclusion
The NGINX cookie flag module provides a reliable way to enforce HttpOnly, Secure, and SameSite attributes on cookies at the reverse proxy layer. It supports all SameSite values including SameSite=None for cross-site cookies, works with all backend types (not just proxy_pass), and never duplicates flags that are already present.
Install it from the GetPageSpeed repository, add a single set_cookie_flag directive, and your cookies gain protection against XSS-based theft, man-in-the-middle interception, and CSRF attacks.
