DEV Community

Cover image for Why Your Nginx Security Headers Disappear (add_header Inheritance Explained)
gokcedemirdurkut
gokcedemirdurkut

Posted on

Why Your Nginx Security Headers Disappear (add_header Inheritance Explained)

Security headers can disappear in Nginx even when they are correctly configured.

Some endpoints may silently drop important headers like:

  • Content-Security-Policy
  • Strict-Transport-Security
  • X-Frame-Options

This behavior is subtle and has existed in Nginx for a long time.


How the Problem Appears

Everything looks correct at first.

curl -I https://example.com/api/health
Enter fullscreen mode Exit fullscreen mode

Response headers include security policies as expected.

But then another endpoint is checked:

curl -I https://example.com/
Enter fullscreen mode Exit fullscreen mode

And suddenly:

❌ Content-Security-Policy missing
❌ Strict-Transport-Security missing
❌ X-Frame-Options missing
Enter fullscreen mode Exit fullscreen mode

Tools like securityheaders.com will immediately detect this.

The confusing part is that the headers are clearly defined in the configuration.


A Common Configuration

A typical setup may look like this:

server {
    add_header Content-Security-Policy "...";
    add_header Strict-Transport-Security "max-age=31536000";

    location ~* \.html$ {
        add_header Cache-Control "no-store";
    }
}
Enter fullscreen mode Exit fullscreen mode

The intention is simple:

  • disable caching for HTML responses
  • apply security headers globally

However, this configuration does not behave as expected.


What Actually Happens

Responses served from:

location ~* \.html$
Enter fullscreen mode Exit fullscreen mode

will only include:

Cache-Control: no-store
Enter fullscreen mode Exit fullscreen mode

All security headers defined in the server block disappear.

There are no warnings and no errors in the logs.
The headers are simply missing.


The Reason

This happens because of how Nginx handles header inheritance.

If a location block defines any add_header directive, it does not inherit add_header directives from parent blocks.

Even adding a single header like:

Cache-Control
Enter fullscreen mode Exit fullscreen mode

causes Nginx to drop previously defined headers such as:

  • Content-Security-Policy
  • Strict-Transport-Security
  • X-Frame-Options
  • Referrer-Policy
  • Permissions-Policy

This behavior has existed for many years and has caused confusion in many configurations.


The Previous Workarounds

Before recently, the usual approaches were:

1. Duplicating headers

Define all security headers again in every location block.

2. Using includes

Create a file like:

include security_headers.conf;
Enter fullscreen mode Exit fullscreen mode

and include it everywhere.

Both approaches work but are difficult to maintain and easy to forget.


The Fix in Nginx 1.29+

Recent Nginx versions introduced a better solution:

add_header_inherit merge;
Enter fullscreen mode Exit fullscreen mode

This directive changes how headers are inherited.

Instead of replacing headers defined in parent blocks, child blocks merge them.

This feature was introduced in newer Nginx releases and is documented in the official release notes:

https://blog.nginx.org/blog/nginx-open-source-1-29-3-and-1-29-4


A Safer Configuration

With the new directive, the configuration can be written like this:

server {

    add_header_inherit merge;

    add_header Content-Security-Policy "...security policy..." always;
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;

    location ~* \.html$ {
        add_header Cache-Control "no-store, no-cache, private, max-age=0" always;
    }

}
Enter fullscreen mode Exit fullscreen mode

Now the behavior is predictable:

  • HTML responses are not cached
  • Security headers are applied consistently
  • Headers do not need to be duplicated

Final Takeaway

When using add_header inside location blocks in Nginx 1.29+, enabling the following directive is recommended:

add_header_inherit merge;
Enter fullscreen mode Exit fullscreen mode

This prevents accidental loss of security headers and makes configurations easier to maintain.

Top comments (0)