Bare nginx — without Plesk, cPanel or any control panel — gives you full control over every header, every cipher, every protocol. This guide walks through every fix our Security Audit raises on a directly-configured nginx server: adding the six recommended HTTP security headers, configuring modern TLS protocols and publishing security.txt. Tested against nginx 1.24 on Ubuntu 22.04 / 24.04 LTS. For Apache, see the Apache variant; for the full finding catalogue, see Security Audit Fixes.
The six headers our audit checks (CSP, HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy) all go inside your server block via add_header. The critical nginx quirk: add_header at parent scope is silently replaced when any add_header appears in an inner block, so put all six in the same location to avoid surprises.
/etc/nginx/sites-available/yourdomain.conf # Debian/Ubuntu convention /etc/nginx/conf.d/yourdomain.conf # CentOS/RHEL/Amazon Linux /etc/nginx/nginx.conf # Single-file deploymentsOpen the file with your preferred editor:
sudo nano /etc/nginx/sites-available/yourdomain.conf
server { ... } block that handles HTTPS (port 443), paste the following six directives. Always include always so the headers apply to error responses too:
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Content-Type-Options "nosniff" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always; add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; frame-ancestors 'self'" always;
sudo nginx -tIf the test passes, reload:
sudo nginx -s reloadOr via systemd:
sudo systemctl reload nginx
curl -sI https://yourdomain.com/ | grep -iE "strict-transport|x-frame|x-content|referrer|permissions|content-security"All six headers should appear in the output. If any are missing, check that the
add_header directives are inside the right server block and that you reloaded (not just edited) nginx.
/etc/nginx/snippets/security-headers.conf and include snippets/security-headers.conf; inside each server block. One source of truth, easy to update.The audit also flags TLS 1.0 and TLS 1.1 — both deprecated, both insecure. nginx defaults vary by distribution, so do not assume your server is already on TLS 1.2+.
nginx.conf http block):
ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers off; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305; ssl_session_cache shared:SSL:10m; ssl_session_timeout 1d; ssl_session_tickets off;
ssl_prefer_server_ciphers off is correct for modern TLS — clients now pick better ciphers than servers do.
RFC 9116 standardises the /.well-known/security.txt file so security researchers know where to report vulnerabilities. Browsers and audit tools both check for it.
sudo mkdir -p /var/www/yourdomain/public/.well-knownCreate
security.txt with at minimum Contact and Expires:
Contact: mailto:security@yourdomain.com Expires: 2027-05-18T00:00:00.000Z Preferred-Languages: en Canonical: https://yourdomain.com/.well-known/security.txtAdjust the email and expiry date to suit. Expiry must be a future date — past expiry invalidates the file.
.txt with the wrong MIME type by default. Force text/plain:
location = /.well-known/security.txt {
default_type text/plain;
}
The = makes it an exact match so it does not interfere with other .well-known paths (such as ACME challenges from Let's Encrypt).
sudo nginx -t && sudo nginx -s reload curl -sI https://yourdomain.com/.well-known/security.txtExpected:
HTTP/2 200 and content-type: text/plain.
After all three sections above are complete, run the Security Audit against your domain. All six headers should pass, TLS protocols should be modern-only, and the security.txt check should show green. Most bare-nginx servers reach 95-100/100 after this guide.
nginx -s reload.Verify every header, TLS protocol and security.txt with a fresh scan.
Run Security Audit →