The NGINX headers-more module gives you complete control over HTTP request and response headers. Unlike the built-in add_header directive, the NGINX headers-more module lets you set, clear, and replace any header. This includes protected headers like Server, Content-Type, and Cache-Control.
Whether you’re hardening your server’s security, integrating with backend services, or implementing caching strategies, the NGINX headers-more module provides flexibility that native NGINX directives lack.
Why Use the NGINX Headers-More Module?
The native add_header directive in NGINX has significant limitations:
| Limitation | add_header |
headers-more |
|---|---|---|
Modify Server header |
No | Yes |
| Clear existing headers | No | Yes |
| Modify request headers | No | Yes |
| Filter by status code | No | Yes |
| Filter by content type | No | Yes |
| Wildcard header patterns | No | Yes |
Works in if blocks |
Clears all headers | Works correctly |
The NGINX headers-more module solves the notorious add_header inheritance problem. Headers disappear when you add a new one inside a nested location block. This module fixes that behavior.
Installation
RHEL, CentOS, AlmaLinux, Rocky Linux
sudo dnf install https://extras.getpagespeed.com/release-latest.rpm
sudo dnf install nginx-module-headers-more
Then add the following to the top of /etc/nginx/nginx.conf:
load_module modules/ngx_http_headers_more_filter_module.so;
Debian and Ubuntu
First, set up the GetPageSpeed APT repository, then install:
sudo apt-get update
sudo apt-get install nginx-module-headers-more
On Debian/Ubuntu, the package handles module loading automatically. No
load_moduledirective is needed.
For more details, see the RPM module page or APT module page.
Directives Reference
The NGINX headers-more module provides four directives for manipulating HTTP headers.
more_set_headers
Sets or replaces response (output) headers.
Syntax: more_set_headers [-t <content-type>]... [-s <status-code>]... <header>...
Context: http, server, location, location if
Phase: Output header filter
This directive sets headers in the response sent to clients. If a header with the same name exists, it gets replaced.
# Set a custom Server header
more_set_headers "Server: MyApp/2.0";
# Set multiple headers at once
more_set_headers "X-Frame-Options: DENY" "X-Content-Type-Options: nosniff";
# Set headers only for specific status codes
more_set_headers -s "500 502 503 504" "X-Error-Handler: true";
# Set headers only for specific content types
more_set_headers -t "text/html text/plain" "X-Text-Content: true";
# Combine status and content-type filters
more_set_headers -s "200" -t "application/json" "X-API-Response: true";
more_clear_headers
Removes response headers entirely.
Syntax: more_clear_headers [-t <content-type>]... [-s <status-code>]... <header>...
Context: http, server, location, location if
Phase: Output header filter
# Remove the Server header (hides NGINX version)
more_clear_headers Server;
# Remove multiple headers
more_clear_headers "X-Powered-By" "X-AspNet-Version";
# Use wildcards to remove all headers matching a pattern
more_clear_headers "X-Debug-*";
The wildcard * character can only appear at the end of header names.
more_set_input_headers
Sets or replaces request (input) headers before they reach your application.
Syntax: more_set_input_headers [-r] [-t <content-type>]... <header>...
Context: http, server, location, location if
Phase: Rewrite tail
Use this directive to modify incoming request headers. This is useful when proxying to backend servers.
location /api {
# Set a custom header for the backend
more_set_input_headers "X-Real-IP: $remote_addr";
# Override the Host header
more_set_input_headers "Host: api.internal.example.com";
proxy_pass http://backend;
}
# Only replace if header already exists (-r flag)
more_set_input_headers -r "Authorization: Bearer $new_token";
The -r flag ensures the header is only modified if it already exists in the request.
more_clear_input_headers
Removes request headers before they reach your application.
Syntax: more_clear_input_headers [-t <content-type>]... <header>...
Context: http, server, location, location if
Phase: Rewrite tail
# Remove potentially dangerous headers before proxying
more_clear_input_headers "X-Forwarded-For" "X-Real-IP";
# Remove all headers matching a pattern
more_clear_input_headers "X-Custom-*";
Security Hardening
System administrators should use the NGINX headers-more module to implement defense-in-depth strategies. Here are the essential security configurations.
Hide Server Information
Attackers scan for specific server versions to exploit known vulnerabilities. Remove or obfuscate server identification headers.
http {
# Completely remove the Server header
more_clear_headers Server;
# OR set a custom, non-revealing value
# more_set_headers "Server: WebServer";
server {
# ...
}
}
Remove Backend Technology Headers
PHP, ASP.NET, and other frameworks often expose version information. Remove all of them using the NGINX headers-more module.
http {
# Remove common framework headers
more_clear_headers "X-Powered-By";
more_clear_headers "X-AspNet-Version";
more_clear_headers "X-AspNetMvc-Version";
more_clear_headers "X-Runtime";
more_clear_headers "X-Version";
# Use wildcards for thoroughness
more_clear_headers "X-Powered-*";
more_clear_headers "X-AspNet-*";
server {
# ...
}
}
Implement Security Headers
The security-headers module handles this automatically. However, you can use the NGINX headers-more module for fine-grained control.
server {
# Prevent MIME type sniffing
more_set_headers "X-Content-Type-Options: nosniff";
# Control iframe embedding
more_set_headers "X-Frame-Options: SAMEORIGIN";
# Enable XSS filter (legacy, but still useful)
more_set_headers "X-XSS-Protection: 1; mode=block";
# Control referrer information
more_set_headers "Referrer-Policy: strict-origin-when-cross-origin";
# Restrict browser features
more_set_headers "Permissions-Policy: geolocation=(), microphone=(), camera=()";
location / {
# ...
}
}
Sanitize Proxy Requests
When using NGINX as a reverse proxy, sanitize incoming headers. This prevents header injection attacks.
server {
location /api {
# Remove client-supplied forwarding headers (prevent spoofing)
more_clear_input_headers "X-Forwarded-For";
more_clear_input_headers "X-Forwarded-Proto";
more_clear_input_headers "X-Forwarded-Host";
more_clear_input_headers "X-Real-IP";
# Set trusted forwarding headers
more_set_input_headers "X-Forwarded-For: $remote_addr";
more_set_input_headers "X-Forwarded-Proto: $scheme";
more_set_input_headers "X-Real-IP: $remote_addr";
proxy_pass http://backend;
}
}
Status-Based Error Headers
Add debugging headers only for error responses. This helps troubleshooting without exposing info on success.
server {
# Add diagnostic headers only for errors
more_set_headers -s "400 401 403 404" "X-Error-Type: client";
more_set_headers -s "500 502 503 504" "X-Error-Type: server";
# Add cache-control for errors
more_set_headers -s "500 502 503 504" "Cache-Control: no-store";
location / {
# ...
}
}
Using Variables in Headers
The NGINX headers-more module supports NGINX variables in header values. This enables dynamic content.
server {
location / {
# Add request tracking
more_set_headers "X-Request-ID: $request_id";
# Add timing information
more_set_headers "X-Request-Time: $request_time";
# Add server information (for debugging)
more_set_headers "X-Server-Name: $hostname";
# Custom application version from a variable
set $app_version "2.1.0";
more_set_headers "X-App-Version: $app_version";
}
}
Variables work only in header values, not in header names. This is for performance.
Content-Type Filtering
Apply headers only to specific content types using the -t flag.
server {
# Add CSP only to HTML responses
more_set_headers -t "text/html" "Content-Security-Policy: default-src 'self'";
# Add CORS headers only to JSON API responses
more_set_headers -t "application/json" "Access-Control-Allow-Origin: *";
# Multiple content types in one directive
more_set_headers -t "text/html text/plain" "X-Text-Response: true";
location / {
# ...
}
}
Important: Don’t include charset in the -t option. Use only the MIME type (e.g., text/html).
Common Use Cases
API Gateway Configuration
server {
listen 443 ssl http2;
server_name api.example.com;
# Remove server information
more_clear_headers Server;
# CORS headers for all API responses
more_set_headers "Access-Control-Allow-Origin: https://app.example.com";
more_set_headers "Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS";
more_set_headers "Access-Control-Allow-Headers: Authorization, Content-Type";
location / {
# Sanitize incoming headers
more_clear_input_headers "X-Internal-*";
# Add request metadata for backend
more_set_input_headers "X-Request-ID: $request_id";
more_set_input_headers "X-Forwarded-For: $remote_addr";
proxy_pass http://api_backend;
}
}
Caching Configuration
server {
location /static/ {
# Set aggressive caching for static assets
more_set_headers "Cache-Control: public, max-age=31536000, immutable";
# Remove unnecessary headers
more_clear_headers "Pragma";
more_clear_headers "Expires";
root /var/www/html;
}
location /api/ {
# Prevent caching of API responses
more_set_headers "Cache-Control: no-store, no-cache, must-revalidate";
more_set_headers "Pragma: no-cache";
proxy_pass http://backend;
}
}
Multi-Tenant Headers
map $host $tenant_id {
tenant1.example.com "tenant-001";
tenant2.example.com "tenant-002";
default "unknown";
}
server {
# Pass tenant identification to backend
more_set_input_headers "X-Tenant-ID: $tenant_id";
location / {
proxy_pass http://backend;
}
}
Limitations
Connection Header
The Connection header cannot be modified. It’s generated by NGINX’s core header filter. This filter runs after headers-more. The Connection header must reflect the actual connection state.
Server Header in Subrequests
The more_clear_headers Server directive affects only the main request. For subrequests, you may still see the Server header.
if Block Restrictions
The more_set_headers directive works in location if blocks. It does not work in server if blocks:
server {
# This does NOT work
if ($request_uri ~* "^/api") {
more_set_headers "X-API: true"; # Will fail!
}
# Use location blocks instead
location /api {
more_set_headers "X-API: true"; # Works correctly
}
}
Performance
The NGINX headers-more module operates at the filter phase with minimal overhead:
- No external I/O: All operations happen in memory
- Lazy registration: Filters register only when directives are used
- Efficient matching: Status and content-type checks use optimized lookups
For most deployments, the module adds sub-millisecond latency. Avoid many wildcard patterns in high-traffic locations.
Testing Your Configuration
After configuring headers-more, verify the results:
# Check response headers
curl -I https://your-server.com/
# Verify Server header is hidden
curl -I https://your-server.com/ | grep -i server
# Check headers for specific status codes
curl -I https://your-server.com/nonexistent-page
# Check API headers
curl -I https://your-server.com/api/health
Validate your configuration before reloading:
nginx -t
systemctl reload nginx
Troubleshooting
Headers Not Appearing
- Check directive context: Ensure the directive is in the correct block
- Check filter order: Proxy modules may override headers after headers-more
- Verify module is loaded: Run
nginx -T 2>&1 | grep headers_more
Headers Appearing Multiple Times
The NGINX headers-more module replaces headers. If you see duplicates, check for:
- Multiple
add_headerdirectives (native NGINX) - Backend servers adding the same headers
- Other modules like
proxy_hide_header
Wildcard Patterns Not Working
Wildcards only work at the end of header names:
# Correct
more_clear_headers "X-Debug-*";
# Incorrect (will not match anything)
more_clear_headers "*-Debug";
more_clear_headers "X-*-Debug";
Conclusion
The NGINX headers-more module is essential for system administrators who need precise header control. Whether you’re implementing security hardening, integrating with APIs, or managing caching, this module provides capabilities that native directives cannot match.
For source code and examples, visit the headers-more-nginx-module repository on GitHub.
Related articles:
– NGINX Security Headers Module – Automatic security headers
– NGINX Cookie Flag Module – Cookie security attributes
– NGINX Bot Protection – Anti-bot measures

