LEMP Field Notes
Security & TLS

Nginx Hardening Checklist for Production Servers

Nginx Hardening Checklist for Production Servers
In briefNginx hardening starts with the effective configuration, not a generic snippet. Inventory package sources and modules, expose only required services, define safe behavior for unknown hosts, separate Nginx and PHP-FPM permissions, bound requests to the application's contract, maintain TLS, minimize sensitive logs, and observe rate limits before enforcing them. Validate each change and keep a tested rollback path. Exact values depend on the installed build and application.

What belongs on an Nginx hardening checklist?

Nginx hardening is a controlled reduction of attack surface: inventory the effective configuration, expose only required services, route unknown hosts safely, separate Nginx from PHP-FPM, bound requests to what the application accepts, maintain TLS, limit sensitive logging, and verify every change with a rollback ready. This checklist uses Ubuntu 24.04 LTS as its reference platform. It contains no command blocks because the configuration was not tested on a clean Ubuntu 24.04 host.

A checklist cannot know which package source, modules, application routes, proxies, upload sizes, or recovery tools exist on a particular server. Record those facts before changing anything.

Gate Evidence to keep Failure the gate is meant to prevent Owner and rollback
Version and config inventory Package origins, enabled modules, effective Nginx and PHP-FPM configuration Applying advice for a different build Named operator; last known-good config
Network exposure Listening services, firewall policy, administrative access route Unnecessary public services System owner; console or recovery access
Host routing Expected names and an explicit default-server policy on every public port Unknown hosts reaching a real site Nginx owner; prior server blocks
Files and runtime Public roots, owners, PHP-FPM pools, writable paths Web processes changing code or reading another site Application owner; permission manifest
Request controls Application-approved methods, body limits, and timeouts Unbounded or invalid traffic consuming resources Application owner; recorded prior values
TLS and headers Certificate scope, renewal evidence, protocol test, header report Expiry, downgrade, or a policy that breaks the app TLS owner; tested prior policy
Abuse controls Baseline traffic, trusted proxy chain, dry-run results Blocking legitimate clients or trusting forged addresses Operations owner; immediate disable path
Logs and monitoring Required fields, retention, alerts, access controls Missing incidents or retaining secrets Operations and privacy owners; prior format

Start with the effective configuration, not a sample file

Record the Ubuntu release, Nginx package source and build, PHP version, PHP-FPM pools, loaded modules, active server blocks, listening addresses, certificate paths, and upstreams. Include configuration brought in through other files. A hardening change reviewed against one visible file can miss an inherited directive or a second virtual host.

Nginx documents a configuration test and a separate option that dumps the complete configuration. Use the platform's approved operational procedure to obtain both syntax-test evidence and the effective configuration. This article does not publish those commands because they were not tested here as written.

Treat a scanner result as an observation, not an implementation plan. Give each finding an owner, proposed control, validation test, and rollback condition.

Harden the Ubuntu host before tuning Nginx

The web server cannot compensate for an exposed administration service or an unpatched operating system. Ubuntu's current security guidance recommends updates, least-privilege accounts, a firewall, secured remote access, and AppArmor. Verify which services listen publicly, which account can administer them, and how recovery works if network access is lost.

Ubuntu says unattended-upgrades is installed by default and security updates are enabled after installation. It also warns that adding a third-party repository does not automatically make that repository eligible for unattended upgrades. Inspect the actual allowed origins and logs instead of assuming every installed package is covered.

AppArmor is installed and loaded by default on Ubuntu, but that does not prove every workload has a useful enforcing profile. Record profile state and denials. Firewall, SSH, update, and AppArmor changes require a system owner and tested recovery path.

For the base stack and its package context, start with the Ubuntu 24.04 LEMP installation guide.

Make host routing fail closed

Nginx selects a virtual server by address, port, and request host. Its documentation states that an unmatched host is handled by the default server for that listening port. Therefore, list every public address and port, identify the default for each, and test an expected name, an unknown name, the server IP, and a request without a normal host value.

The default path must be deliberate. It should not expose a production application merely because that server block appears first in a file. Keep document roots specific to one site, outside user-upload or backup directories, and confirm that directory indexing is not enabled where no listing is intended. Search public roots for environment files, archives, database exports, editor copies, source maps, and deployment artifacts before launch.

The Nginx server-block guide explains virtual-host structure. Every public listener also needs a tested unknown-host outcome.

Separate Nginx, PHP-FPM, code, and writable data

Run application code and Nginx workers without administrative privileges. Give the web path only the access required by the application. Code and configuration normally need read access, while uploads, caches, sessions, and generated files need narrowly defined writable locations. A recursive world-writable permission change is not a hardening method.

Separate applications into PHP-FPM pools when they need distinct users, limits, logs, or deployment ownership. The PHP manual documents that clear_env defaults to enabled, which prevents arbitrary environment variables from reaching workers unless added to the pool configuration. It also documents security.limit_extensions, whose default permits .php and .phar; review the effective value against the application's entry points and the Nginx-to-FPM mapping.

Keep an FPM status endpoint off the public Internet. If operations needs it, restrict it to the monitoring path and authorized clients. Record socket or loopback ownership, Nginx access, pool user, application write paths, and cross-site isolation as separate checks.

Bound requests to the application's real contract

Set request-body size, accepted methods, header and body timeouts, and upstream timeouts from application requirements and observed traffic. There is no universal secure value. An upload service, an API, and a read-only documentation site have different contracts.

Nginx documents a client_max_body_size default of 1m; a request above the configured limit receives a 413 response, while a value of 0 disables the check. That documented default is a fact about the directive, not a recommendation. The application owner should supply the largest legitimate request, expected method set, slowest valid operation, and proxy path before an operator changes the values.

Test normal requests, boundary-sized requests, invalid methods, slow upstream behavior, and the application's own error handling. A lower limit that rejects valid backups or media is an outage. A very high limit chosen to make the error disappear removes the control.

Reduce information exposure without mistaking it for a boundary

Nginx documents server_tokens on as the default. Turning version emission off removes the version from Nginx-generated error pages and the Server response header field. It does not patch the service, remove every product clue, or hide information added by an upstream application.

PHP's expose_php setting similarly controls PHP identification in web-server headers. PHP's own security documentation describes this as a limited information-reduction measure, not a security control by itself. Verify the response at the public edge, including errors and redirects, but keep update status and exposure reduction as separate checklist lines.

Public responses should avoid stack traces, local paths, queries, secrets, and internal service names. Preserve actionable detail in access-controlled logs.

Treat TLS and response headers as application changes

Maintain certificate coverage, renewal automation, private-key access, redirects, and the deployed certificate chain. Confirm renewal with the actual automation path and monitor the certificate served at the edge, not merely a file on disk. The existing Certbot and Nginx guide covers certificate installation.

Do not copy a cipher list or protocol block whose origin and platform support are unknown. Use current guidance for the installed TLS library and Nginx build, then test representative clients. Do not enable HTTP Strict Transport Security until the owner understands its persistence and the effect on every covered hostname.

Content Security Policy, framing controls, referrer policy, content-type handling, and permissions policy are application-dependent. Inventory scripts, styles, frames, APIs, fonts, media, and third-party origins before enforcement. Start with report-only behavior where the policy supports it, inspect violations, and obtain application-owner approval before blocking. A green header scanner does not prove the application remains usable.

Add rate limiting only after the client identity is trustworthy

Nginx's request-limiting module applies a leaky-bucket method to a configured key and supports dry-run observation. The appropriate key, rate, burst behavior, and exclusions depend on the application. No fixed value belongs in a general checklist.

First establish whether Nginx sees the actual client or a reverse proxy. Trust only known proxy addresses and verify the resulting client identity in logs. Otherwise, one shared proxy address can make unrelated users look like one client, while an untrusted forwarded header can make the limit ineffective.

Observe candidate rules before enforcement. Account for login endpoints, APIs, health checks, webhooks, shared networks, and legitimate bursts. Define the enforcement metric and rollback threshold. Rate limiting is not authentication or a denial-of-service guarantee.

Keep logs useful and proportionate

Nginx access logs can include status, request length, bytes sent, and request time. Choose the minimum fields required for operations and incident review. Avoid logging authorization values, session identifiers, passwords, full sensitive query strings, or request bodies. Check application and proxy logs as well; sensitive data often enters upstream of Nginx.

Define ownership, permissions, rotation, retention, clock synchronization, alerting, and deletion for each log. Monitor changes in error rate, upstream failures, rejected requests, certificate expiry, disk use, restart failures, and update failures. A log file with no alert or review owner is storage, not monitoring.

Make every production change reversible

Before editing, preserve the previous configuration through the team's approved version-control or backup process and confirm how it will be restored. Make one coherent change at a time. Validate syntax and the effective configuration, exercise the affected routes in a staging or canary path, reload through the approved service procedure, then inspect process state, logs, response headers, certificates, and application transactions.

Do not proceed when recovery access is uncertain. Firewall, remote-access, permission, TLS, and process-user changes can remove administrative or application access. A named operator must own the observation window and rollback decision.

Record the old and new evidence, reviewer, deployment time, validation results, exceptions, and rollback outcome. Hardening is maintained state, not a one-time edit.

Final production review

Sources

An independent publication. Not affiliated with any prior owner of this domain.

FAQ

Does hiding the Nginx version make a server secure?

No. Disabling version emission reduces information in Nginx-generated errors and the Server response header, but it does not patch Nginx, secure the operating system, or remove clues exposed by an upstream application. Track version exposure and update status as separate controls, and verify the public response path after any change.

What should client_max_body_size be in Nginx?

Set it from the largest legitimate request the application accepts, with an explicit margin and a boundary test. Nginx documents a 1m default and returns 413 above the configured value; setting it to 0 disables the check. Those are directive facts, not a universal production recommendation. Upload services and read-only sites need different limits.

Should every Nginx server use the same security headers?

No. Header policies depend on the application's scripts, styles, frames, APIs, media, and third-party origins. Inventory those dependencies, use report-only behavior where available, inspect violations, and obtain application-owner approval before enforcement. A copied Content Security Policy can break legitimate behavior while still looking strict in a configuration review.

Is rate limiting always safe to enable?

No. Verify the client address through any trusted proxy, measure normal traffic, and observe a proposed rule before enforcement. Shared networks, health checks, webhooks, APIs, and legitimate bursts can otherwise be blocked. The key, rate, burst behavior, and exceptions are application-specific; a general checklist cannot supply safe universal values.

How do I harden Nginx without locking myself out?

Confirm recovery access first, preserve the last known-good configuration, assign a rollback owner, and make one coherent change at a time. Validate syntax and the effective configuration, test affected routes, then monitor service state, logs, certificates, headers, and application transactions. Stop when rollback or console access is uncertain.

Is this a tested Nginx configuration guide?

No. It is a documentation-based audit checklist using Ubuntu 24.04 LTS as the reference platform. It intentionally contains no command blocks because the configuration was not tested on a clean Ubuntu 24.04 host. Record the installed package sources, modules, application requirements, and rollback process before translating any checkpoint into a local change.