nginx-proxy-manager
nginx-proxy-manager copied to clipboard
How to use 'Custom Nginx Configuration' function to modify headers for added security
Checklist
- Please read the setup instructions Done
- Please read the FAQ Done
What is troubling you?
In short, I am having some trouble using the Custom Nginx Configuration.
I just want to start by saying this is a great tool for noobs like me who are just getting into self-hosting and reverse proxy management. As a beginner, I am slightly paranoid about the security of my server and is chasing this elusive A+ header test (see https://securityheaders.com/) for the domain that I am hosting (I am currently getting an E grade with the default NPM with 'Block common exploits' enabled).
After some googling, I learnt that it will be useful to include the following NGINX configurations for added security
add_header X-Xss-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Strict-Transport-Security "max-age=2592000; includeSubdomains" always;
add_header X-Frame-Options "SAMEORIGIN" always;
proxy_hide_header X-Powered-By;
add_header 'Referrer-Policy' 'no-referrer';
add_header Content-Security-Policy "frame-ancestors mydomain.com files.mydomain.com;";
The issue is when I dump them to Custom Nginx Configuration section of the GUI, nothing happens. If I try to add {} to the script, NPM will go offline.
{add_header X-Xss-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Strict-Transport-Security "max-age=2592000; includeSubdomains" always;
add_header X-Frame-Options "SAMEORIGIN" always;
proxy_hide_header X-Powered-By;
add_header 'Referrer-Policy' 'no-referrer';
add_header Content-Security-Policy "frame-ancestors mydomain.com files.mydomain.com;";}
Can someone shed some light into how I could go about adding these headers?
Much appreciated.
Ed
That's a very good question. Your first attempt should be working (no brackets), but it's not. I'm currently investigating this; I noticed for example that NPM adds a header "X-Served-By $host;" but it's not there either, so I suspect some aspect of nginx I don't know.
I just tried using this from an old site of mine and it's working. It shows up on https://securityheaders.com/ as functioning.
add_header Strict-Transport-Security "max-age=31536000; includeSubdomains;"; add_header X-Frame-Options "SAMEORIGIN"; add_header X-XSS-Protection "1; mode=block"; add_header X-Content-Type-Options nosniff; add_header Referrer-Policy same-origin; add_header Content-Security-Policy "default-src 'self' https://.elementor.com https://.google.com; font-src 'self' data: https://.googleapis.com https://.gstatic.com; img-src 'self' data: https://i.imgur.com https://.gravatar.com https://.elementor.com; object-src 'none'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline' data: https://.googleapis.com; script-src-elem 'self' 'unsafe-inline' https://.cloudflare.com; frame-src 'self' https://.youtube.com https://.google.com;";
I tried your code as well and it's working. The only way that I could get it too work though is pictured below. If I tried too list the domain name it would not register.
After some digging, I think it's because of an oddity in the way nginx handles headers configuration: https://www.peterbe.com/plog/be-very-careful-with-your-add_header-in-nginx
Was this ever sorted out? I'm struggling to get the exact headers added to mine. I've even tried adding it to custom/server_proxy.conf with no luck. From my troubleshooting, I think something is overwriting it.
Any news about this? It's a quite important feature that should work
I'm also curious if anyone has found a solution... I'm having the same problem. At one point I came across this thread and I got it working using the custom location as demonstrated above.
But today it stopped working (ie the headers are no longer working... I'm using it with CloudFlare if that's relevant) when I had to renew the certs.
+1
I am facing the exact same dilemma. Adding any header = "add_header" variables to "Edit Proxy Host" / Advanced Has no effect.
As I noted in my post from Sep 23, 2020, the problem is from Nginx itself, not NPM.
Well on my setup, those headers are in the main server block, not any location block. But they're still not being added. I copied these into the custom config section of the site definition in npm without any effect.
As I noted in my post from Sep 23, 2020, the problem is from Nginx itself, not NPM.
No... I'm pretty sure it's a NGINX Proxy Manager bug. It's already in the code, but it's not working...
Code: https://github.com/jc21/nginx-proxy-manager/blob/1a64d44857b8db488f56567b743f787b61e1f7a4/backend/app.js#L34-L52
When add the headers manually to a proxy_host config ( below # HSTS) it works perfectly fine...
add_header Referrer-Policy "no-referrer" always;
add_header X-Frame-Options SAMEORIGIN;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
I created a workaround which works pretty well... More info: Workaround - Security Headers @ NGINX Proxy Manager
Result:
As I noted in my post from Sep 23, 2020, the problem is from Nginx itself, not NPM.
Hi I am pretty sure its not NGINX, I setup NGINX SSL Proxies + WAF's often, if I manually add the headers to the server blocks, all works as expected, but this is not ideal as NPM is supposed to be the "easier" option vs custom / scratch setups.
Or if you could please give a more productive answer i.e. point me to where the NGINX issue is, I can then investigate or be more informed.
Kind Regards
@jacqueshenning imo the person above you states what does work.
Is this still an issue by the way? (as I am yet to configure the settings stated above).
Hi,
I'm also using custom location and I'm getting A+ grade with these headers:
add_header X-Xss-Protection "1; mode=block" always; add_header X-Content-Type-Options "nosniff" always; add_header X-Frame-Options "SAMEORIGIN" always; proxy_hide_header X-Powered-By; add_header 'Referrer-Policy' 'no-referrer'; add_header Permissions-Policy "accelerometer=();ambient-light-sensor=(); autoplay=();camera=();encrypted-media=();focus-without-user-activation=(); geolocation=();gyroscope=();magnetometer=();microphone=();midi=();payment=();picture-in-picture=(); speaker=();sync-xhr=();usb=();vr=()"; add_header Content-Security-Policy "default-src 'none'; style-src 'self' ; form-action 'self'; frame-ancestors 'self'; base-uri 'self'; img-src 'self' data:; font-src 'self'; frame-src 'self'";
and also
include conf.d/include/ssl-ciphers.conf; in the advanced tab otherwise it uses unsafe ciphers if I use a custom SSL cert instead of Let's Encrypt
@Kopernikus1979 thanks for this, works like a charm!
@Kopernikus1979 thanks for this, works like a charm!
Your welcome :-)
It's possible that the add_header Content-Security-Policy "default-src 'none'; style-src 'self' ; form-action 'self'; frame-ancestors 'self'; base-uri 'self'; img-src 'self' data:; font-src 'self'; frame-src 'self'"; needs tweaking (define allowed) for some services/apps, otherwise youc an replace it with add_header Content-Security-Policy upgrade-insecure-requests;
The only way i got this working is by manually adjusting the .conf. Can anyone explain what the custom location even means? What exactly is the IP address supposed to be?
Hi @andrewwarz - The custom location is located inside Nginx Proxy Manager where you create the proxy hosts.
I have added the following to my configuration.
add_header X-Xss-Protection "1; mode=block" always; add_header X-Content-Type-Options "nosniff" always; add_header X-Frame-Options "SAMEORIGIN" always; proxy_hide_header X-Powered-By; add_header 'Referrer-Policy' 'no-referrer'; add_header Permissions-Policy "accelerometer=();ambient-light-sensor=(); autoplay=();camera=();encrypted-media=();focus-without-user-activation=(); geolocation=();gyroscope=();magnetometer=();microphone=();midi=();payment=();picture-in-picture=(); speaker=();sync-xhr=();usb=();vr=()"; add_header Content-Security-Policy upgrade-insecure-requests;
Hope it helps.
Tested this myself and i cant get more than B rating.
I have tried the workaround that @R0GGER posted and what @NiapApa said, both failed me.
Is there any progress done on this?
I'm on npm version 2.9.19 and the problem is still there
Also on 2.9.19 and none of the workarounds specified above work.
Installed v2.10.2 and followed @R0GGER workaround. Thx
I found adding 'always' on the end of for nginx works :-
{% if certificate and certificate_id > 0 -%} {% if ssl_forced == 1 or ssl_forced == true %} {% if hsts_enabled == 1 or hsts_enabled == true %} add_header Strict-Transport-Security "max-age=63072000;{% if hsts_subdomains == 1 or hsts_subdomains == true -%} includeSubDomains;{% endif %} preload" always; add_header Referrer-Policy strict-origin-when-cross-origin always; add_header X-Content-Type-Options "nosniff" always; add_header X-XSS-Protection "1; mode=block" always; add_header X-Frame-Options "SAMEORIGIN" always; add_header Content-Security-Policy upgrade-insecure-requests always; add_header Permissions-Policy interest-cohort=() always; add_header Expect-CT 'enforce; max-age=604800' always; more_set_headers 'Server: Proxy'; more_clear_headers 'X-Powered-By'; {% endif %} {% endif %} {% endif %}
I hope it helps....
Just use headers more.
more_set_headers "X-XSS-Protection: 1; mode=block"; more_set_headers "X-Content-Type-Options: nosniff"; more_set_headers "Referrer-Policy: no-referrer-when-downgrade"; more_set_headers "Content-Security-Policy: default-src 'self' http: https: ws: wss: data: blob: 'unsafe-inline'; frame-ancestors 'self';"; more_set_headers "Permissions-Policy: interest-cohort=()"; more_set_headers "Strict-Transport-Security: max-age=31536000; includeSubDomains";
Having an issue with adding custom headers, whenever i add a custom location "/" and add the headers there i get this page when trying to visit the site.
damn, is this still an issue 2024???
Yeah, still not fixed you can add them manually like I do
If someone is here for Nextcloud, as I am, here's the solution: Just paste this in the NPM Advanced tab for your hostname:
location /.well-known/carddav {
return 301 $scheme://$host/remote.php/dav;}
location /.well-known/caldav {
return 301 $scheme://$host/remote.php/dav;}
more_set_headers "X-XSS-Protection: 1; mode=block";
more_set_headers "X-Content-Type-Options: nosniff";
more_set_headers "X-Robots-Tag: noindex, nofollow";
more_set_headers "Referrer-Policy: no-referrer-when-downgrade";
more_set_headers "Content-Security-Policy: default-src 'self' http: https: ws: wss: data: blob: 'unsafe-inline'; frame-ancestors 'self';";
more_set_headers "Permissions-Policy: interest-cohort=()";
more_set_headers "Strict-Transport-Security: max-age=31536000; includeSubDomains";
more_set_headers "X-Frame-Options: SAMEORIGIN";
more_set_headers "X-Permitted-Cross-Domain-Policies: none";