[GH-ISSUE #68] Duplicate HSTS headers #65

Closed
opened 2026-02-26 05:34:02 +03:00 by kerem · 10 comments
Owner

Originally created by @pageb018 on GitHub (Feb 12, 2019).
Original GitHub issue: https://github.com/NginxProxyManager/nginx-proxy-manager/issues/68

Not sure if I am missing something. I added

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

to the custom NGINX section of a proxy host config. I can see the line added to the .conf files, but when running an SSL labs test it doesn't seem to apply.

Any ideas?

Originally created by @pageb018 on GitHub (Feb 12, 2019). Original GitHub issue: https://github.com/NginxProxyManager/nginx-proxy-manager/issues/68 Not sure if I am missing something. I added `add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;` to the custom NGINX section of a proxy host config. I can see the line added to the .conf files, but when running an SSL labs test it doesn't seem to apply. Any ideas?
kerem 2026-02-26 05:34:02 +03:00
  • closed this issue
  • added the
    bug
    label
Author
Owner

@jc21 commented on GitHub (Feb 18, 2019):

HSTS is already declared in all SSL Proxy Hosts with the following:

add_header Strict-Transport-Security max-age=15768000;

however this doesn't include subdomains and isn't always set, so there might be room for this configuration to move to the frontend and be controlled by the UI.

Furthermore, there is the following "quirk" with nginx:

NGINX configuration blocks inherit add_header directives from their enclosing blocks, so you just need to place the add_header directive in the top‑level server block. There’s one important exception: if a block includes an add_header directive itself, it does not inherit headers from enclosing blocks, and you need to redeclare all add_header directives.

This means that, no matter which add_header lines put in the advanced config, it will be discarded in the location blocks and they will need to be redeclared again; which you can't do in the UI currently.

Thanks for finding this process error. It will need to be fixed properly.

<!-- gh-comment-id:464532969 --> @jc21 commented on GitHub (Feb 18, 2019): HSTS is already declared in all SSL Proxy Hosts with the following: ``` add_header Strict-Transport-Security max-age=15768000; ``` however this doesn't include subdomains and isn't always set, so there might be room for this configuration to move to the frontend and be controlled by the UI. Furthermore, there is the following "quirk" with nginx: > NGINX configuration blocks inherit add_header directives from their enclosing blocks, so you just need to place the add_header directive in the top‑level server block. There’s one important exception: if a block includes an add_header directive itself, it does not inherit headers from enclosing blocks, and you need to redeclare all add_header directives. This means that, no matter which `add_header` lines put in the advanced config, it will be discarded in the location blocks and they will need to be redeclared again; which you can't do in the UI currently. Thanks for finding this process error. It will need to be fixed properly.
Author
Owner

@pageb018 commented on GitHub (Feb 19, 2019):

Just an FYI. Not sure if it matters but SSL Labs are still throwing an error:

"Server sent invalid HSTS policy. See below for further information."

"Strict Transport Security (HSTS) | Invalid   Server provided more than one HSTS header"

<!-- gh-comment-id:465135240 --> @pageb018 commented on GitHub (Feb 19, 2019): Just an FYI. Not sure if it matters but SSL Labs are still throwing an error: "Server sent invalid HSTS policy. See below for further information." "Strict Transport Security (HSTS) | Invalid   Server provided more than one HSTS header"
Author
Owner

@jc21 commented on GitHub (Feb 20, 2019):

So, after pulling the latest docker tag and editing your Proxy Host and saving it with HSTS enabled, you should be able to inspect the headers of the requests yourself and make sure there only 1 header.

After the latest release, all SSL enabled Hosts need to be edited and saved so that the new configuration would be generated for them.

<!-- gh-comment-id:465365383 --> @jc21 commented on GitHub (Feb 20, 2019): So, after pulling the `latest` docker tag and editing your Proxy Host and saving it with HSTS enabled, you should be able to inspect the headers of the requests yourself and make sure there only 1 header. After the latest release, all SSL enabled Hosts need to be edited and saved so that the new configuration would be generated for them.
Author
Owner

@pageb018 commented on GitHub (Feb 20, 2019):

So it does look like this is being applied twice. When I enable in the GUI, i see the following in the .conf

 # HSTS (ngx_http_headers_module is required) (31536000 seconds = 1 year)
  add_header Strict-Transport-Security "max-age=31536000;includeSubDomains; preload" always;




  access_log /data/logs/proxy_host-10.log proxy;



  location / {# Access List
    auth_basic            "Authorization required";
    auth_basic_user_file  /data/access/1;


    # Force SSL
    include conf.d/include/force-ssl.conf;




  # HSTS (ngx_http_headers_module is required) (31536000 seconds = 1 year)
  add_header Strict-Transport-Security "max-age=31536000;includeSubDomains; preload" always;
<!-- gh-comment-id:465567086 --> @pageb018 commented on GitHub (Feb 20, 2019): So it does look like this is being applied twice. When I enable in the GUI, i see the following in the .conf ``` # HSTS (ngx_http_headers_module is required) (31536000 seconds = 1 year) add_header Strict-Transport-Security "max-age=31536000;includeSubDomains; preload" always; access_log /data/logs/proxy_host-10.log proxy; location / {# Access List auth_basic "Authorization required"; auth_basic_user_file /data/access/1; # Force SSL include conf.d/include/force-ssl.conf; # HSTS (ngx_http_headers_module is required) (31536000 seconds = 1 year) add_header Strict-Transport-Security "max-age=31536000;includeSubDomains; preload" always; ```
Author
Owner

@jc21 commented on GitHub (Feb 21, 2019):

Oh ok yeah so I did that on purpose... Because nginx doc (mentioned above) says that it will discard any add_header items from the server block when there is at least 1 used in a location block, I added it to both and the first one is meant to be discarded, but I guess it isn't. I'll have to test it further.

<!-- gh-comment-id:465820723 --> @jc21 commented on GitHub (Feb 21, 2019): Oh ok yeah so I did that on purpose... Because nginx doc (mentioned above) says that it will discard any `add_header` items from the `server` block when there is at least 1 used in a `location` block, I added it to both and the first one is meant to be discarded, but I guess it isn't. I'll have to test it further.
Author
Owner

@pageb018 commented on GitHub (Feb 21, 2019):

Cool. Thanks for checking.

By the way, thank you for this awesome piece of software. It's fantastic and so much better than writing .conf files!

I also wanted to confirm that certs auto-renew...I don't see that in the docs.

thanks again.

<!-- gh-comment-id:466037433 --> @pageb018 commented on GitHub (Feb 21, 2019): Cool. Thanks for checking. By the way, thank you for this awesome piece of software. It's fantastic and so much better than writing .conf files! I also wanted to confirm that certs auto-renew...I don't see that in the docs. thanks again.
Author
Owner

@rudders commented on GitHub (Feb 23, 2019):

+1 for the auto-renew of certs questions - I can't seem to find that in the doco..

<!-- gh-comment-id:466687841 --> @rudders commented on GitHub (Feb 23, 2019): +1 for the auto-renew of certs questions - I can't seem to find that in the doco..
Author
Owner

@jc21 commented on GitHub (Feb 25, 2019):

Auto renewal is definitely happening. When the docker image is started you'll see a log entry:

[2/18/2019] [10:43:20 PM] [SSL      ] › ℹ  info      Let's Encrypt Renewal Timer initialized
<!-- gh-comment-id:466833768 --> @jc21 commented on GitHub (Feb 25, 2019): Auto renewal is definitely happening. When the docker image is started you'll see a log entry: ``` [2/18/2019] [10:43:20 PM] [SSL ] › ℹ info Let's Encrypt Renewal Timer initialized ```
Author
Owner

@chaptergy commented on GitHub (Oct 25, 2021):

SSLlabs no longer trows this error, presumably because of a newer version of nginx which handles this more gracefully.

<!-- gh-comment-id:950936809 --> @chaptergy commented on GitHub (Oct 25, 2021): SSLlabs no longer trows this error, presumably because of a newer version of nginx which handles this more gracefully.
Author
Owner

@nickelswitte commented on GitHub (May 7, 2023):

I dont know why, but I still have the duplication issue. I just wanted to add some headers via the advanced config, and they all appear twice in the sent response by the server. It does not seem that nginx will discard the previous one.

The config in npm

location / {
  # Force SSL
  include conf.d/include/force-ssl.conf;
  # HSTS (ngx_http_headers_module is required) (63072000 seconds = 2 years)
  add_header Strict-Transport-Security "max-age=63072000;includeSubDomains; preload" always;
  add_header X-Frame-Options "ALLOW-FROM my.domain.tld";
  add_header Content-Security-Policy "frame-ancestors my.domain.tld";
  # Proxy!
  include conf.d/include/proxy.conf;
}

The response:

HTTP/2 200 OK
server: openresty
date: Sun, 07 May 2023 10:32:39 GMT
content-type: text/html
content-encoding: gzip
x-frame-options: sameorigin
x-xss-protection: 1; mode=block
x-content-type-options: nosniff
content-security-policy: default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; connect-src 'self'; manifest-src 'self'; media-src 'self' blob:; child-src 'self' blob:; frame-src 'self'; frame-ancestors 'none'
referrer-policy: same-origin
permissions-policy: interest-cohort=()
x-frame-options: ALLOW-FROM my.do.main
content-security-policy: frame-ancestors my.do.main
X-Firefox-Spdy: h2
<!-- gh-comment-id:1537404275 --> @nickelswitte commented on GitHub (May 7, 2023): I dont know why, but I still have the duplication issue. I just wanted to add some headers via the advanced config, and they all appear twice in the sent response by the server. It does not seem that nginx will discard the previous one. The config in npm ``` location / { # Force SSL include conf.d/include/force-ssl.conf; # HSTS (ngx_http_headers_module is required) (63072000 seconds = 2 years) add_header Strict-Transport-Security "max-age=63072000;includeSubDomains; preload" always; add_header X-Frame-Options "ALLOW-FROM my.domain.tld"; add_header Content-Security-Policy "frame-ancestors my.domain.tld"; # Proxy! include conf.d/include/proxy.conf; } ``` The response: ``` HTTP/2 200 OK server: openresty date: Sun, 07 May 2023 10:32:39 GMT content-type: text/html content-encoding: gzip x-frame-options: sameorigin x-xss-protection: 1; mode=block x-content-type-options: nosniff content-security-policy: default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; connect-src 'self'; manifest-src 'self'; media-src 'self' blob:; child-src 'self' blob:; frame-src 'self'; frame-ancestors 'none' referrer-policy: same-origin permissions-policy: interest-cohort=() x-frame-options: ALLOW-FROM my.do.main content-security-policy: frame-ancestors my.do.main X-Firefox-Spdy: h2 ```
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/nginx-proxy-manager-NginxProxyManager#65
No description provided.