- Published on
Nginx add_header
In **Nginx**, `add_header` is used to **add HTTP response headers** (for security, caching, CORS, debugging, etc.).
Below is a **clear, practical guide** with common pitfalls and best practices.
---
## Basic syntax
```nginx
add_header <header-name> <value> [always];
Example:
add_header X-Custom-Header "Hello World";
This adds to the response headers sent to the client.
⚠️ Important rule (most people get this wrong)
add_headeronly applies to successful responses (2xx, 3xx) by default
That means 404 / 500 / 401 responses will NOT include the header unless you use always.
Correct way (recommended)
add_header X-Frame-Options "DENY" always;
Scope & inheritance (VERY important)
add_header does NOT inherit the way you might expect.
Rule
- If any
add_headerexists at a lower level, all parentadd_headers are ignored
Hierarchy:
http
└── server
└── location
❌ Common mistake
server {
add_header X-A "A";
location / {
add_header X-B "B";
}
}
Result in /:
- ❌
X-Ais missing - ✅ Only
X-Bexists
✅ Correct way
server {
add_header X-A "A";
location / {
add_header X-A "A";
add_header X-B "B";
}
}
Common real-world examples
1. Security headers
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'" always;
2. CORS headers
add_header Access-Control-Allow-Origin "*" always;
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS" always;
add_header Access-Control-Allow-Headers "Authorization, Content-Type" always;
For OPTIONS preflight:
if ($request_method = OPTIONS) {
add_header Content-Length 0;
add_header Content-Type text/plain;
return 204;
}
3. Disable caching
add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate" always;
add_header Pragma "no-cache" always;
add_header Expires "0" always;
4. Debug / inspection header
add_header X-Served-By $hostname always;
Overriding or removing headers
Replace a header
Just redefine it:
add_header Cache-Control "max-age=3600" always;
Remove headers (needs headers_more module)
more_clear_headers Server;
more_clear_headers X-Powered-By;
proxy_set_header ≠ add_header
This is a very common confusion:
| Directive | Purpose |
|---|---|
add_header | Adds headers to response sent to client |
proxy_set_header | Sets headers sent to upstream server |
Example:
proxy_set_header X-Forwarded-For $remote_addr;
⬆️ This does not affect client response headers.
Quick checklist (best practice)
✅ Always use always ✅ Repeat headers at each level where you use add_header ✅ Put global headers at server or http level ❌ Don’t assume inheritance ❌ Don’t confuse with proxy_set_header