I have an Nginx proxy setup where I add several security-related headers to the server so that they return on all proxy locations. On some locations I need to add additional headers (ex. Content-Security-Policy to /), while on other specific locations I need to remove one of the headers (ex. X-Frame-Options from /framepage.html) added at the server level.


# ...

server {
  # ...

  include security-headers.conf;

  location / {
    proxy_pass http://web:5000/;
    include security-headers.conf;
    add_header Content-Security-Policy "my csp...";

  location = /framepage.html {
    proxy_pass http://web:5000/framepage.html;
    # TODO: remove `X-Frame-Options` response header from this specific page
    # Tried add_header X-Frame-Options "";
    # Tried proxy_set_header X-Frame-Options "";
    # Tried proxy_hide_header X-Frame-Options;

  location /api/ {
    proxy_pass http://api:5000/;

  location /otherstuff/ {
    proxy_pass http://otherstuff:5000/;

  # ...


add_header Referrer-Policy same-origin;
add_header X-Frame-Options SAMEORIGIN;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";

I have tried the following, but none of them seem to remove the X-Frame-Options header from the /framepage.html location response:

  • add_header X-Frame-Options "";
  • proxy_set_header X-Frame-Options "";
  • proxy_hide_header X-Frame-Options;

How can I remove the X-Frame-Options header from the /framepage.html location response?

  • Is your backend server sending this header, then? Sep 2, 2018 at 20:47
  • @MichaelHampton no, it is only set by the parent server block include statement.
    – kspearrin
    Sep 2, 2018 at 20:49

6 Answers 6


The header config attributes are a bit confusing, this is what they do:

proxy_set_header is to set a request header
add_header is to add a header to the response
proxy_hide_header is to hide a response header

If you want to replace a header that already exists in the response it is not enough with add_header because it will stack the values (from server and the one you added).

You have to do this in two steps:

1) remove header:
proxy_hide_header Access-Control-Allow-Origin;

2) add your custom header value:
add_header Access-Control-Allow-Origin "*" always;

  • 3
    help, proxy_hide_header works with proxy_pass, it doesn't work with 'return'
    – Dan D.
    Jan 24, 2020 at 8:35
  • @datdinhquoc What do you mean with "it doesn't work with 'return'"? Could you elaborate or even better post a question and send me a link?
    – Wilt
    Jan 24, 2020 at 10:31
  • i want to change a response header, but 'return' in server block will pass the url to browser and some http 302 happen in browser, i cant add the extra header to the final redirected url
    – Dan D.
    Jan 24, 2020 at 18:20
  • 1
    This only works for response headers set by an upstream server but not for headers set by nginx self like "server" header for example.
    – Daniel W.
    Aug 3, 2020 at 15:21

You can use the headers_more module. Example:

location / {
    proxy_pass http://upstream_server/;
    more_clear_headers 'Access-Control-Allow-Origin';



You could probably try to use the 3rd party "Headers More" module:


And something along the lines of:

load_module modules/ngx_http_headers_more_filter_module.so;

http {
    more_clear_headers 'X-Frame-Options';

Don't include security-headers.conf at the server level. Only include it in each individual location where you want these headers to be sent.

The reason for this is that add_header directives are inherited from the previous level if and only if the current level has no add_header directives. Thus, your including them in the server block causes them to be included in every location as you aren't overriding them in any location.

  • 1
    Yes, I was actually doing this intentionally since I wanted them to apply to all locations without having to include it at every location. It sounds like what I am trying to do is not possible.
    – kspearrin
    Sep 2, 2018 at 21:20
  • Sure it is, just follow my instructions and DO NOT put it in the server or in the location where you don't want the headers sent. Sep 2, 2018 at 21:23

Using the Nginx Lua module.

You will need the nginx-extras package installed. For example in aptitude do a apt install nginx-extras.

In nginx.conf:

load_module /usr/lib/nginx/modules/ndk_http_module.so;
load_module /usr/lib/nginx/modules/ngx_http_lua_module.so;

http {

  header_filter_by_lua_block {
    ngx.header["server"] = nil

To verify run a nginx -V and you will see http-lua. The ndk_http_module.so is needed to load the ngx_http_lua_module.so module.

Always best to run an nginx -t to verify your configuration, as well.


I had the exact same issue.

We have global CSP rules at the nginx.conf (http) level which then needed to be removed at the server level / virtualhost level to let the proxied CSP header - generated by the app - surface (otherwise we got 2 x CSP headers and the most restrictive applies).

The below worked for me.

server {
  listen           443 ssl;
  server_name      foobar.domain.tld;

  # Custom CSP or Clear CSP
  # Remove default CSP from nginx.conf for this custom one. 
  add_header Content-Security-Policy "";

  location / {
      proxy_pass ...

Hope it helps.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .