Hardening nginx
nginx is different from everything else on this site: being reachable from the internet is not a misconfiguration, it’s the job. There is no “bind it to loopback” answer here, and no firewall rule that closes the exposure.
So hardening nginx is about what it does with the traffic it is supposed to accept — what it negotiates, what it tells the browser, what it serves for requests you didn’t anticipate, and what it hands to the filesystem.
8 controls
- Severity: high
- Severity: high
- Severity: medium
- Severity: medium
- Severity: medium
- Severity: low
- Severity: low
- Severity: low
Two pieces of standard advice are now wrong
Section titled “Two pieces of standard advice are now wrong”Most nginx hardening guides were written some years ago and copied since. Two of their staples have expired, and both are on this list:
X-XSS-Protectionis deprecated and can introduce vulnerabilities. It is not merely useless. See security headers.- OCSP stapling does nothing if you use Let’s Encrypt, which stopped including OCSP URLs in certificates and shut the responder down in August 2025. See OCSP stapling.
If a guide recommends both without qualification, it hasn’t been revisited in a while — including, quite possibly, your own config.
The trap that undoes the rest of this page
Section titled “The trap that undoes the rest of this page”nginx’s add_header does not accumulate. Directives are inherited from the
enclosing block only if the current block defines no add_header of its own.
Add a single header inside a location, and every security header from the
server block silently disappears for that location.
Nothing warns you. The config is valid, nginx reloads happily, and one path on your site quietly serves no security headers at all. This catches almost everyone, and it is covered in full on security headers.
nginx or freenginx?
Section titled “nginx or freenginx?”Worth knowing, though it changes little here. In 2024 a longtime core developer forked nginx as freenginx, over disagreements with F5 about how security issues are handled. The two develop in parallel and track similar version numbers — nginx 1.30.x stable and 1.31.x mainline as of mid-2026, with freenginx at 1.30.x/1.31.x too.
Everything in this cluster applies to both; the configuration language is the same. Check what you actually run:
nginx -vWhere the config lives
Section titled “Where the config lives”nginx -t # test the config and print the paths it usednginx -T # test, and dump the FULL effective config including includesnginx -T is the important one. Debian and Ubuntu split configuration across
/etc/nginx/nginx.conf, /etc/nginx/conf.d/*.conf and
/etc/nginx/sites-enabled/*, so the file you are editing is rarely the whole
picture. Every verification on this site uses nginx -T rather than reading a
single file.
Reloading
Section titled “Reloading”sudo nginx -t && sudo systemctl reload nginxAlways test first. A reload with a broken config leaves the old workers running and logs the error — recoverable. A restart with a broken config leaves you with no web server at all. Never restart what you can reload.