Join our Newsletter — 33% off our NHI Course
Home› Guides› NGINX Security Hardening Guide: Directives, TLS, Access Control…
Guide Network Security

NGINX Security Hardening Guide: Directives, TLS, Access Control and Logging

← All guides
By NHI Mgmt Group, edited by Lalit Choda Updated 27 October 2026 14 min read
On this page

NGINX sits in front of a large share of web applications and APIs as web server, reverse proxy and TLS terminator, and a few unsafe defaults or one misplaced directive can expose files, leak versions, or send traffic to backends without verifying who they are. This guide sets out a hardening baseline for open source NGINX and notes where NGINX Plus and Kubernetes ingress controllers differ. It covers who can change the configuration, how NGINX authenticates clients and upstreams, the secrets it holds, logging and detection, and a directive-level baseline taken from the official nginx.org documentation. It is for platform engineers, web operations teams and application security staff, and it builds on the Proxies and Forward vs Reverse Proxy Security Guide rather than repeating it.

Key takeaways

  • The configuration is the attack surface. NGINX has no admin console in the open source build: whoever can write to the configuration directory and run a reload controls every site it serves.
  • Upstream TLS is not verified by default. proxy_ssl_verify defaults to off, so a reverse proxy that re-encrypts to backends accepts any certificate unless you turn verification on.
  • Modern defaults help, explicit settings help more. Since 1.27.3 the default ssl_protocols is TLSv1.2 and TLSv1.3, but older builds and copied snippets still enable TLSv1 and TLSv1.1.
  • Private keys and status endpoints need protecting. TLS keys, htpasswd files, upstream client certificates, stub_status and the NGINX Plus API are high-value targets and must be restricted.
  • Ingress controllers are a different risk class. CVE-2025-1974 in the community ingress-nginx controller (CVSS 9.8) showed that a controller holding cluster-wide Secret access turns a configuration injection into cluster compromise, and that project's maintenance ended in March 2026.

How identity and access work in NGINX

NGINX makes three kinds of trust decision: who may change it, which clients may reach a resource, and which upstream servers it will trust. It has no user database of its own. Access decisions come from modules in the configuration.

MechanismModule and directivesUse it forNotes from the documentation
Address rulesallow, deny (access module)Admin paths, status pages, internal APIsEvaluated in order; end with deny all
HTTP Basic authenticationauth_basic, auth_basic_user_fileSmall internal tools onlySupports crypt, apr1 and {SSHA}; the docs say not to use PLAIN or unsalted SHA. Only over HTTPS
External authorisationauth_request, auth_request_setDelegating to an SSO or OAuth proxySubrequest 2xx allows, 401 or 403 denies; not built by default (--with-http_auth_request_module)
Client certificatesssl_verify_client, ssl_client_certificateService-to-service and device accessSee the mTLS Guide
JWT and OIDCauth_jwt, auth_oidcToken validation at the edgeNGINX Plus modules; the satisfy directive lists them alongside the open source access modules
Method restrictionlimit_exceptRead-only endpointslimit_except GET also permits HEAD
Combining rulessatisfy all | anyAddress plus password, or eitherDefault all. any lets a request pass if one module allows it, a common cause of accidental exposure

Use internal on locations that should only be reached by internal redirects, such as error pages or files served through X-Accel-Redirect: the core module documentation says external requests to them return 404.

Admin roles and privileged access

In open source NGINX, administration means file system and process control. The master process normally starts as root to bind ports 80 and 443 and read keys, then worker processes run as the unprivileged account named in the user directive. Privilege therefore sits with whoever can edit nginx.conf and its included files, write to the document root, replace the binary or modules, or run nginx -s reload.

  • Separate config writers from operators: Keep configuration in version control and deploy it through a pipeline that runs nginx -t before a reload. Interactive root access to production web servers should be the exception, logged through your privileged access tooling. See the Privileged Access Workstations Guide.
  • File ownership: Configuration, keys and htpasswd files owned by root and not writable by the worker user; document roots not writable by the worker user unless an upload path genuinely needs it, and then served with no script execution.
  • Dynamic modules: load_module loads shared objects into the server. Restrict the modules directory to root and only load modules you have reviewed.
  • NGINX Plus API: The commercial api module with write=on accepts POST, PATCH and DELETE to add or remove upstream servers and change key-value zones. That is a live control plane. The documentation's example binds it to 127.0.0.1 with allow 127.0.0.1; deny all;; keep it on loopback or a management interface and put authentication in front of it.
  • Management tools and agents: Central management agents and dashboards that push configuration hold the same power as root on every instance. Treat their credentials and service accounts as privileged.

Integrations, API tokens and service accounts

NGINX holds and passes machine credentials that are easy to overlook in an access review.

CredentialWhere it livesControl
TLS private keysssl_certificate_key filesThe nginx.org HTTPS guide says keys need restricted access but must be readable by the master process. Root-owned, mode 600 or 400, automated renewal. See the ACME and Automated Certificate Management Guide
Upstream client certificateproxy_ssl_certificate, proxy_ssl_certificate_keyUnique per proxy tier, short-lived, rotated with the backend trust store
Static API keys injected into upstream requestsproxy_set_header Authorization ... in configurationAvoid secrets in config files; if unavoidable, keep them in an included file with restricted permissions, sourced from a secrets manager at deploy time. See the Secrets Management Guide
htpasswd entriesauth_basic_user_fileSalted hashes only; move to SSO through auth_request where users are people
Ingress controller service accountKubernetes RBAC for the controller podLimit Secret read to namespaces it serves where the controller supports it. See the Kubernetes RBAC Guide
Session ticket keysssl_session_ticket_keys filesRotate regularly or disable tickets; a stolen ticket key undermines forward secrecy for resumed sessions

When NGINX authenticates users through auth_request and passes identity to backends with auth_request_set and proxy_set_header, the backend must accept those headers only from the proxy. Strip any client-supplied header with the same name, and lock the backend to the proxy's address or to mTLS, otherwise a client can forge identity by calling the backend directly.

Kubernetes ingress

The NVD record for CVE-2025-1974 says an unauthenticated attacker with access to the pod network could run arbitrary code in the ingress-nginx controller and read the Secrets it can access, which by default means all Secrets cluster-wide. The Kubernetes project released fixes in ingress-nginx 1.12.1 and 1.11.5 on 24 March 2025, and later announced that best-effort maintenance of the community Ingress NGINX controller would end in March 2026, after which there are no releases, bug fixes or security fixes. Confirm which controller you run (the community project is distinct from other NGINX-based controllers), plan migration to a maintained controller or the Gateway API, restrict network access to admission webhooks, and narrow the controller's Secret access. See the Kubernetes Security Guide and the Kubernetes Admission Control Guide.

Logging and detection

  • Structured access logs: The predefined combined format logs client address, user, time, request line, status, bytes, referrer and user agent. Add $request_time, $upstream_addr, $upstream_status, $ssl_protocol, $ssl_cipher and a request ID, and use log_format ... escape=json (since 1.11.8) so fields parse reliably in the SIEM.
  • Ship off the host: access_log syslog:server=... or a log agent sends logs off-box, so an attacker with server access cannot quietly edit them.
  • Real client address: Behind a load balancer, use the realip module with set_real_ip_from limited to the load balancer ranges; trusting X-Forwarded-For from anywhere lets clients spoof their address in logs and access rules.
  • Error log: Keep error_log at warn or error in production and collect it: upstream TLS failures, limit_req rejections and permission errors appear there.
  • Rate limit telemetry: limit_req_dry_run on (since 1.17.1) and the $limit_req_status variable let you measure what a limit would reject before enforcing it.

What to alert on

  • Requests for dotfiles, backups and path traversal sequences that return 200 (for example /.git/, /.env, .. after a static prefix).
  • Spikes of 401, 403, 413, 429 or 503 from one source, which indicate credential guessing or limit testing.
  • Requests to stub_status, the Plus API or admin paths from outside the allowed ranges.
  • Configuration file changes and reloads outside the deployment pipeline, detected by file integrity monitoring on /etc/nginx.
  • New executable files in document roots, a sign of a web shell (ATT&CK T1505.003).

Detection content for these logs is in the Web Server Access Log Detection Guide.

Metrics

  • Baseline compliance: Percentage of instances passing an automated check of the baseline below.
  • Patch latency: Days from an nginx.org security advisory to deployment of the fixed release.
  • Upstream verification: Percentage of HTTPS upstreams with proxy_ssl_verify on.
  • Exposed internals: Count of status, API or admin locations reachable from the internet (target zero).
  • Out-of-pipeline changes: Number of configuration changes not traced to a reviewed commit.

Hardening baseline

AreaDirective or actionDefaultRecommended
Version disclosureserver_tokensonoff
Client TLS protocolsssl_protocolsTLSv1.2 TLSv1.3 from 1.27.3; earlier releases also enabled TLSv1 and TLSv1.1Set TLSv1.2 TLSv1.3 explicitly, or TLSv1.3 only where all clients support it. See the TLS Server Configuration Baseline Guide
Ciphersssl_ciphersHIGH:!aNULL:!MD5Restrict TLS 1.2 to forward-secret AEAD suites
Upstream TLS verificationproxy_ssl_verify, proxy_ssl_trusted_certificate, proxy_ssl_server_nameoff, none, offon, internal CA bundle, on
Request body sizeclient_max_body_size1mSmallest value each location needs; never 0 (unlimited) globally
Slow client timeoutsclient_header_timeout, client_body_timeout, send_timeout, keepalive_timeout60s, 60s, 60s, 75sLower (for example 10 to 20 seconds) on internet-facing servers to resist slow-request exhaustion
Header bufferslarge_client_header_buffers4 8kKeep defaults unless an application needs more
Rate limitinglimit_req_zone $binary_remote_addr ..., limit_req, limit_req_status 429None; rejections return 503Limits on login, search and API paths. See the Rate Limiting and API Abuse Guide
Symlinksdisable_symlinksoffif_not_owner or on on shared hosting and upload paths
Directory listingautoindexoffKeep off
Methodslimit_exceptAll methodsOnly the methods each location needs
Status and APIstub_status, apiNot built by default; Plus API read-only unless write=onLoopback or management network only, with allow/deny
Response headersadd_header ... alwaysNoneHSTS, content type options and framing controls. See the Security Headers Guide

Rollout, testing and rollback

  1. Inventory: List every NGINX instance, version, build options (nginx -V shows compiled modules) and role (web server, reverse proxy, ingress, API gateway).
  2. Patch first: Move to a currently supported release. nginx.org publishes security advisories with fixed versions on both current release branches, so track that page.
  3. Codify the baseline: Put the directives above in a shared included file, keep site files small, and lint every change with nginx -t in CI.
  4. Test in report mode: Enable new rate limits with limit_req_dry_run on, scan TLS with a client test tool, and turn on proxy_ssl_verify in staging to find backends with bad certificates.
  5. Canary: Deploy to one instance or a small share of traffic, watch error rates and 4xx and 5xx trends for a day, then roll out in rings.
  6. Rollback: Keep the previous configuration commit deployable; a failed reload leaves the old workers running, and reverting the commit plus a reload restores service.

Choosing between options

  • Basic auth or auth_request: Basic auth is acceptable for a single internal tool with a handful of users over HTTPS; anything used by staff at scale should use auth_request to an SSO proxy so MFA, joiner and leaver processes apply.
  • TLS at NGINX or end to end: Terminating at NGINX and re-encrypting with verified upstream TLS suits most estates; mTLS to backends is worth the certificate effort where backends are shared or in another trust zone.
  • Small versus large estates: A small team can manage one hardened include file by hand with nginx -t; large estates need configuration generated from templates, policy checks in CI and drift detection on every host.

Framework mapping

AreaNIST SP 800-53 Rev. 5NIST CSF 2.0ISO/IEC 27001:2022 Annex A
Secure baseline and least functionalityCM-6, CM-7PR.PS-018.9 Configuration management
Client and upstream TLSSC-8PR.DS-028.24 Use of cryptography
Access control on locationsAC-3PR.AA-058.3 Information access restriction
Rate limits and timeoutsSC-5PR.IR-018.20 Networks security
PatchingSI-2ID.RA-018.8 Management of technical vulnerabilities
LoggingAU-12DE.CM-098.15 Logging

Relevant ATT&CK techniques: T1190 Exploit Public-Facing Application, T1505.003 Web Shell, T1499.002 Service Exhaustion Flood and T1592 Gather Victim Host Information (version banners).

Common misconfigurations

  • Alias without matching slashes: The core documentation advises keeping location and alias paths consistent and preferring root where the location matches the end of the path. A pattern such as location /static with alias /var/www/static/; can let requests like /static../ reach the parent directory. See the Path Traversal Guide.
  • Re-encrypting without verifying: proxy_pass https:// with the default proxy_ssl_verify off gives the appearance of encrypted backends with no protection against a spoofed upstream (ATT&CK T1557 Adversary-in-the-Middle).
  • satisfy any by accident: Combining an IP allow list with Basic auth under satisfy any opens the location to anyone with a password from anywhere, or to anyone on the allowed network without one.
  • Trusting forwarded headers: set_real_ip_from 0.0.0.0/0 or passing client-supplied identity headers to backends lets attackers bypass address rules or impersonate users.
  • Exposed internals: stub_status, the Plus API, .git directories or backup files served from the document root because no location blocks them.
  • Unlimited bodies and long timeouts: client_max_body_size 0 and default 60-second timeouts on internet-facing listeners make resource exhaustion cheap. Pair server limits with upstream protection from the DDoS Protection Guide.
  • Unmaintained ingress controller: Running a controller whose project no longer ships security fixes, with cluster-wide Secret read and an admission webhook reachable from every pod.

Practitioner checklist

  • Inventory every NGINX instance and ingress controller with version, modules and role, and patch to a supported release.
  • Restrict configuration, key and htpasswd files to root, deploy configuration only through a pipeline that runs nginx -t.
  • Set server_tokens off, explicit ssl_protocols TLSv1.2 TLSv1.3 and a forward-secret cipher list.
  • Turn on proxy_ssl_verify with a trusted CA bundle and proxy_ssl_server_name on for every HTTPS upstream.
  • Bind stub_status and the Plus API to loopback or a management network with allow/deny.
  • Replace Basic auth for staff applications with auth_request to SSO, and strip client-supplied identity headers.
  • Set body size limits, shorter client timeouts and limit_req on login and API paths, tested first in dry-run mode.
  • Ship JSON-escaped access and error logs off the host and alert on traversal, status page access and out-of-pipeline reloads.
  • Confirm which Kubernetes ingress controller you run, plan migration off the retired community controller, and narrow its Secret access.

Standards and references

Related NHI Mgmt Group resources: Proxies and Forward vs Reverse Proxy Security Guide · Apache HTTP Server Hardening Guide · TLS Server Configuration Baseline Guide · Web Server Access Log Detection Guide · Kubernetes Security Guide

Free weekly newsletter

Subscribe to the NHI & AI Identity Journal

The latest on NHI and Agentic AI security – articles, research, breaches, news and events every week.

Bonus 33% off our NHI Course when you subscribe.

NHIMG Editorial Note
Researched and written by the NHI Mgmt Group editorial team using AI tools, under the editorial direction of Lalit Choda. Facts are drawn from the sources listed on this page and checked by our automated quality process. Last updated 27 October 2026. Spotted an error? Use our contact page.
NHI Mgmt Group, the independent authority on Non-Human Identity, IAM and Agentic AI security. nhimg.org