How to Remove Specific Response Headers in Nginx Proxy Configurations: X-Frame-Options Case Study


2 views

When working with Nginx proxy configurations, headers added at the server level get inherited by all locations unless explicitly overridden. This becomes problematic when you need to:

  • Remove security headers for specific endpoints
  • Maintain different security policies per route
  • Allow iframe embedding on selective pages

The attempted solutions don't work because:

# Doesn't work - adds empty header instead of removing
add_header X-Frame-Options "";

# Doesn't work - affects request headers, not response
proxy_set_header X-Frame-Options "";

# Doesn't work - only hides upstream headers
proxy_hide_header X-Frame-Options;

To properly remove server-level headers in specific locations:

location = /framepage.html {
    proxy_pass http://web:5000/framepage.html;
    
    # Clear inherited headers by redefining with empty value
    add_header X-Frame-Options "";
    
    # Required to prevent inheriting server-level headers
    more_clear_headers "X-Frame-Options";
}

For this to work, you need:

  1. Nginx compiled with headers-more module (standard in most distributions)
  2. Ensure the module is loaded in nginx.conf:
load_module modules/ngx_http_headers_more_filter_module.so;

Here's a full configuration demonstrating header removal:

# security-headers.conf
add_header X-Frame-Options SAMEORIGIN always;
add_header Content-Security-Policy "default-src 'self'" always;

# nginx.conf
server {
    listen 80;
    include security-headers.conf;
    
    location /secure {
        proxy_pass http://backend;
        # Inherits all security headers
    }
    
    location /embed {
        proxy_pass http://backend;
        # Remove X-Frame-Options for iframe embedding
        more_clear_headers "X-Frame-Options";
        
        # Add special CSP for embedded content
        add_header Content-Security-Policy "frame-ancestors *";
    }
}

For complex scenarios, consider:

  • Using maps for conditional headers
  • Creating separate security header files per location
  • Leveraging NJS scripting for dynamic header control

When working with Nginx proxy configurations, headers added at the server level automatically propagate to all locations. This becomes problematic when you need to selectively remove security headers like X-Frame-Options for specific endpoints while maintaining them globally.

Most developers first try these approaches that don't work:

# Doesn't work - adds empty header instead of removing
add_header X-Frame-Options "";

# Doesn't work - affects request headers, not response
proxy_set_header X-Frame-Options "";

# Doesn't work - only affects upstream headers
proxy_hide_header X-Frame-Options;

Nginx processes headers in a specific order. To properly remove a header, you must:

  1. Clear all inherited headers in the location block
  2. Re-add only the headers you want

Here's the correct implementation:

location = /framepage.html {
    proxy_pass http://web:5000/framepage.html;
    
    # Step 1: Clear all headers
    include security-headers.conf;
    add_header X-Frame-Options "";
    
    # Step 2: Rebuild headers as needed
    add_header Referrer-Policy same-origin;
    add_header X-Content-Type-Options nosniff;
    add_header X-XSS-Protection "1; mode=block";
    # Explicitly omit X-Frame-Options
}

For more complex scenarios, consider using Nginx maps:

map $uri $frame_options {
    ~^/framepage.html   "";
    default             "SAMEORIGIN";
}

server {
    # ...
    add_header X-Frame-Options $frame_options;
    # ...
}

Always verify header changes using:

curl -I https://yoursite.com/framepage.html

Or browser developer tools' Network tab to inspect response headers.

While this solution works, be aware that:

  • Repeating headers in multiple locations increases config complexity
  • Changes require careful testing to avoid security loopholes
  • Consider using includes for maintainability