22

I have a reverse proxy on nginx which proxies quite a few sites. I have recently enabled HTTP Strict Transport Security for all SSL-enabled websites. I now have one site that doesn't want to have this enabled.

I thought I would just do a simple check if my upstream already sent me a Strict-Transport-Security-header, and if not, just add one. That way, my upstream could send an STS header containing max-age=0 to avoid having HSTS enabled by the proxy.

I thought I'd just change my configuration as follows:

location / {
        proxy_pass http://webservers;

        proxy_redirect off;

        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header X-Forwarded-Proto "https";

        if ($upstream_http_strict_transport_security = "") {
                add_header Strict-Transport-Security "max-age=15552000";
        }
}

But, probably because if is evil, this doesn't work. I have tried a bunch of different things to make sure the variable actually exists (which is the case), but nothing seems to help.

How could I make this work?

Gerry
  • 343
  • 1
  • 2
  • 4

2 Answers2

29

This doesn't work because the if is evaluated before the request is passed to the backend, so the $upstream_http_ variables don't have any values yet. add_header with an empty value is ignored, so you can use a map to conditionally add the header like so:

map $upstream_http_strict_transport_security $sts {
  '' max-age=15552000;
}

server {
  location / {
    add_header Strict-Transport-Security $sts;
  }
}
kolbyjack
  • 7,854
  • 2
  • 34
  • 29
  • 1
    Clever use of map! – Alexey Ten May 23 '14 at 18:20
  • ok i've been trying to do something little different but i couldn't figure nginx out. Can you tell me where can i learn nginx formally. And also can you help me here: http://serverfault.com/questions/678521/nginx-how-to-set-host-header-to-the-server-chosen-load-balancing – Muhammad Umer Mar 26 '15 at 18:34
  • Note that this doesn't work if you have any other directives setting the same header—nginx only passes through the original header if it's not being overridden anywhere. If you encounter this, you can add `default '';` to your map, e.g. `map $upstream_http_strict_transport_security $sts { '' max-age=15552000; default ''; }` – Matt Browne Jul 08 '20 at 21:26
7

That's because if is executed before proxy take place and at this moment there is no variable $upstream_http_strict_transport_security.

You could use header_filter_by_lua directive from Lua module. E.g.

header_filter_by_lua 'if not ngx.header["X-Test"] then ngx.header["X-Test"] = "blah" end';
Alexey Ten
  • 7,922
  • 31
  • 35
  • 1
    This works. On Debian 7 you can get LUA support by using Nginx from backports via `apt-get -t wheezy-backports install nginx-extras`. – Pieter Ennes Apr 10 '15 at 09:38