Custom response headers let Nginx stamp every reply with security policy, cache instructions, routing hints, or integration metadata without changing application code. That is useful when a site needs consistent browser behavior, an operator needs proof of which layer served a request, or a proxy must attach a site-wide policy before the response leaves the server.
In Nginx, response headers are added with add_header name value [always]; inside the http, server, location, or if-inside-location contexts. Without always, the directive applies only to selected success and redirect status codes, so headers can disappear from locally generated 404 and 500 responses. Multiple add_header lines are allowed, but inheritance changes when a child context defines its own header directives.
The clearest place to start is the target site's server block so the change stays scoped to one hostname or application. The examples use the common Debian and Ubuntu layout (/etc/nginx/sites-available/ and the nginx service name). If the same header already comes from the application, a CDN, or another proxy layer, keep one source of truth so clients do not receive duplicate policy lines.
Related: How to improve Nginx security
Related: How to enable HSTS in Nginx
Common site-wide security examples are X-Frame-Options: SAMEORIGIN, X-Content-Type-Options: nosniff, and Referrer-Policy: strict-origin-when-cross-origin.
$ sudoedit /etc/nginx/sites-available/example.com
Site configs are commonly stored under /etc/nginx/sites-available/ (symlinked into /etc/nginx/sites-enabled/) or /etc/nginx/conf.d/ depending on the distribution.
server {
listen 80;
listen [::]:80;
server_name example.com;
root /var/www/html;
location / {
try_files $uri $uri/ =404;
}
}
server {
listen 80;
listen [::]:80;
server_name example.com;
root /var/www/html;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
location / {
try_files $uri $uri/ =404;
}
}
Policies such as Content-Security-Policy, Permissions-Policy, and Strict-Transport-Security can break embeds, third-party scripts, OAuth flows, or subdomains when values are too strict. Validate them in staging before broad rollout.
location /api/ {
add_header Content-Security-Policy "default-src 'self'" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
try_files $uri =404;
}
If a nested block defines any add_header directive, older builds stop inheriting headers from the parent block. When nginx -t reports unknown directive "add_header_inherit", repeat the required headers manually in the child block.
Current upstream Nginx documentation says add_header_inherit merge; became available in version 1.29.3 and appends parent headers to the current block instead of replacing them. A fresh Ubuntu 26.04 package check reported nginx/1.28.3 (Ubuntu), so packaged hosts may still need the manual repetition pattern.
$ sudo nginx -t nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful
$ sudo systemctl reload nginx
On systems that do not use systemd, sudo nginx -s reload asks the running master process to reload the configuration.
$ curl -I --silent http://127.0.0.1/index.html HTTP/1.1 200 OK Server: nginx/1.28.3 (Ubuntu) Date: Sat, 06 Jun 2026 11:22:36 GMT Content-Type: text/html Content-Length: 20 Last-Modified: Sat, 06 Jun 2026 11:22:36 GMT Connection: keep-alive ETag: "6a2402fc-14" X-Frame-Options: SAMEORIGIN X-Content-Type-Options: nosniff Referrer-Policy: strict-origin-when-cross-origin Accept-Ranges: bytes
Use the real site URL instead of 127.0.0.1 when a reverse proxy, load balancer, or CDN sits in front of Nginx and the final client response matters more than the local origin response.
Tool: HTTP Header Checker
$ curl -I --silent http://127.0.0.1/does-not-exist HTTP/1.1 404 Not Found Server: nginx/1.28.3 (Ubuntu) Date: Sat, 06 Jun 2026 11:22:36 GMT Content-Type: text/html Content-Length: 162 Connection: keep-alive X-Frame-Options: SAMEORIGIN X-Content-Type-Options: nosniff Referrer-Policy: strict-origin-when-cross-origin
If the headers appear on the 200 OK response but disappear on the 404 response, the relevant add_header lines are probably missing the always parameter.
Repeated lines usually mean the same header is being set in more than one layer. Keep one authoritative source or hide upstream copies with proxy_hide_header or fastcgi_hide_header where appropriate.