I've recently been focusing on improving the security of the services I host—both internally for myself and externally for clients. As part of this process, I conducted several vulnerability scans using tools like Kali Linux, SecurityHeaders.com, and Probely.

One recurring issue was that Nginx Proxy Manager lacks essential security headers by default. These headers play a critical role in defending against common web-based attacks.


🚨 Missing Security Headers

Out of the box, Nginx Proxy Manager does not set several recommended headers, including:

Header Purpose
Content-Security-Policy Prevents cross-site scripting (XSS) and data injection by restricting resource loading.
X-Frame-Options Protects against clickjacking by controlling if your site can be embedded in an iframe.
X-Content-Type-Options Stops browsers from MIME-sniffing a response away from the declared content-type.
Referrer-Policy Controls how much referrer information is included with requests.
Permissions-Policy Limits access to browser features like microphone, camera, and geolocation.
Expect-CT Instructs browsers to enforce Certificate Transparency.

🛡️ Why Security Headers Matter

Security headers add an extra layer of protection for web users by instructing browsers how to handle your content. Without them, users are more vulnerable to attacks like:

  • Cross-site scripting (XSS)
  • Clickjacking
  • MIME sniffing
  • Data leaks via referrer info

Think of them as browser-level defenses that complement your firewall, HTTPS, and reverse proxy setup.


⚙️ How to Add Security Headers to Nginx Proxy Manager

Nginx Proxy Manager allows you to define custom NGINX configuration snippets on a per-host basis. Here's an example snippet I now use on all my reverse proxy entries that utilize SSL:

{% if certificate and certificateid > 0 -%}
  {% if sslforced == 1 or sslforced == true %}
    {% if hstsenabled == 1 or hstsenabled == true %}
      add_header Strict-Transport-Security "max-age=63072000;{% if hstssubdomains == 1 or hstssubdomains == true -%} includeSubDomains;{% endif %} preload" always;
      add_header Referrer-Policy strict-origin-when-cross-origin;
      add_header X-Content-Type-Options nosniff;
      add_header X-XSS-Protection "1; mode=block";
      add_header X-Frame-Options SAMEORIGIN;
      add_header Content-Security-Policy upgrade-insecure-requests;
      add_header Permissions-Policy interest-cohort=();
      add_header Expect-CT 'enforce; max-age=604800';
      more_set_headers 'Server: Proxy';
      more_clear_headers 'X-Powered-By';
    {% endif %}
  {% endif %}
{% endif %}

Make sure you paste this into the Advanced > Custom Nginx Configuration field in NPM for each proxy host.

💡 Tip: If you're using Docker, restart your stack to apply the changes:

✅ Verifying Your Configuration

After restarting, run a new scan using:

You should now see all critical headers marked as present, and your score significantly improved (typically A+).

Consider taking before and after screenshots to confirm the improvements.


🧩 Final Thoughts

Securing Nginx Proxy Manager is an easy yet crucial step toward hardening your self-hosted services. With just a few lines of configuration, you can dramatically reduce your exposure to common web vulnerabilities.


📝 Like this post?
Read more real-world DevOps and self-hosting stories at:
👉 blog.opensourceitsolutions.co.uk