Nginx for WordPress – Configuration, FastCGI Cache, and Security
Published: April 14, 2026 · Author: Marcin Szewczyk-Wilgan
Nginx is the web server of choice for most high-performance WordPress deployments. Its event-driven, non-blocking architecture handles thousands of concurrent connections with minimal memory overhead – a fundamentally different approach from Apache's process-per-connection model. Nginx 1.30, released in April 2025, brought HTTP/2 to upstream (PHP-FPM) connections, Encrypted Client Hello (ECH), and Multipath TCP (MPTCP) support. This article covers the complete Nginx setup for WordPress: from the core server block configuration through FastCGI caching, security headers, and rate limiting for the login page.
Nginx Architecture and WordPress
Understanding Nginx's architecture explains why it performs so differently from Apache under load.
index.php. In Nginx: try_files $uri $uri/ /index.php?$args;. This single line replaces the multi-line mod_rewrite block that WordPress adds to .htaccess on Apache.expires 1y for versioned assets (with content hashes in filenames) and expires 7d for WordPress media uploads. Add add_header Cache-Control "public, immutable" for cache-busted assets.Core Server Block Configuration
The foundation of a WordPress Nginx configuration covers SSL, PHP passthrough, WordPress rewrites, and static file handling.
listen 443 ssl http2; and listen [::]:443 ssl http2; enable HTTPS with HTTP/2 for IPv4 and IPv6. Always include a redirect from port 80: server { listen 80; return 301 https://$host$request_uri; }.ssl_protocols TLSv1.2 TLSv1.3;. A strong cipher suite: ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;. Enable OCSP stapling with ssl_stapling on; to reduce TLS handshake latency..php requests to PHP-FPM: location ~ \.php$ { try_files $uri =404; fastcgi_pass unix:/var/run/php/php8.3-fpm-wordpress.sock; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; }. The try_files $uri =404 check before passing prevents PHP execution of non-existent files – a critical security measure.location ~* (wp-config\.php|\.htaccess|\.env|xmlrpc\.php) { deny all; }. Block access to upload directories from executing PHP: location ~* /(?:uploads|files)/.*\.php$ { deny all; } – this prevents webshells uploaded through file upload vulnerabilities from being executed.FastCGI Cache
Nginx FastCGI cache stores full-page HTML responses and serves them without invoking PHP. For anonymous visitors on cacheable pages, this can reduce response times from 200–500ms to under 5ms.
Setting up the cache zone
Define a cache zone in nginx.conf: fastcgi_cache_path /var/cache/nginx/wordpress levels=1:2 keys_zone=WORDPRESS:100m inactive=60m max_size=2g;. This creates a 2 GB disk cache with a 100 MB key zone in RAM. Cache files are organized into a 2-level directory structure for efficient filesystem performance.
Always bypass for logged-in users
Critical: set $skip_cache 0; then skip for logged-in users (wordpress_logged_in_* cookie), cart pages, checkout, POST requests, and query strings. if ($http_cookie ~* "wordpress_logged_in") { set $skip_cache 1; }. Use fastcgi_cache_bypass $skip_cache; and fastcgi_no_cache $skip_cache;.
TTL and stale-while-revalidate
Set fastcgi_cache_valid 200 301 302 60m; for a 60-minute cache lifetime. For news sites, shorter TTLs (5–10 minutes) balance freshness with performance. For static content sites, 24 hours is appropriate. Add an X-Cache-Status $upstream_cache_status response header to confirm cache hits in production.
Clearing on content updates
When a post is published or updated, its cached version must be invalidated. The Nginx Helper plugin (by Automattic/rtCamp) integrates with WordPress hooks to purge specific cached URLs on publish. Alternatively, send a DELETE request to fastcgi_cache_purge from a WordPress action hook in custom code.
Security Headers
HTTP security headers are a quick win for WordPress security. They are configured once in Nginx and applied to every response.
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;Tells browsers to always use HTTPS for this domain, even if the user types HTTP. The
preload flag allows submission to browser HSTS preload lists. Start with a short max-age (e.g., 3600) to verify there are no HTTP-only resources before committing to one year.add_header X-Content-Type-Options "nosniff" always;Prevents browsers from MIME-sniffing a response away from its declared content type. Stops attacks where an uploaded file with a harmless extension is executed as JavaScript by a browser that guesses its type.
add_header X-Frame-Options "SAMEORIGIN" always;Prevents the site from being embedded in an iframe on another domain, blocking most clickjacking attacks. Use
DENY if you never need iframing, SAMEORIGIN if you embed your own content. The frame-ancestors CSP directive is the modern replacement but X-Frame-Options maintains broader compatibility.add_header Referrer-Policy "strict-origin-when-cross-origin" always;Controls how much referrer information is sent with requests.
strict-origin-when-cross-origin sends the full URL for same-origin requests but only the origin (without path) for cross-origin requests, reducing privacy leakage.add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;Restricts access to browser APIs. For most WordPress sites that do not use camera, microphone, or geolocation, disabling these APIs reduces the attack surface if XSS vulnerabilities are present.
Rate Limiting for wp-login.php
Nginx rate limiting is the most efficient way to stop brute-force attacks on the WordPress login page. Implemented at the web server level, it stops requests before PHP executes – far more efficient than plugin-based limiting.
nginx.conf within the http {} block: limit_req_zone $binary_remote_addr zone=wp_login:10m rate=5r/m;This creates a 10 MB memory zone tracking request rates per client IP, allowing 5 requests per minute to the login page.
location = /wp-login.php { limit_req zone=wp_login burst=3 nodelay; try_files $uri =404; fastcgi_pass unix:/run/php/php8.3-fpm-wordpress.sock; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; }The
burst=3 allows a burst of 3 requests before rate limiting activates.location = /xmlrpc.php { deny all; }This stops all XML-RPC-based brute-force attacks (which are typically more efficient than direct wp-login.php attacks due to the multicall method).
location ~* /wp-(admin|login\.php) { allow 203.0.113.0/24; deny all; }. Attackers without an allowed IP cannot even reach the login form.Nginx 1.30 Features
Nginx 1.30 (April 2025) introduced several features relevant to WordPress hosting:
PHP-FPM over HTTP/2
Nginx 1.30 added the ability to communicate with upstream servers (including PHP-FPM via FastCGI over HTTP/2). This reduces connection overhead when proxying to PHP-FPM on high-concurrency sites. Requires PHP-FPM configuration to accept HTTP/2 connections.
Encrypted Client Hello
Encrypted Client Hello (ECH) encrypts the TLS handshake's Server Name Indication (SNI), preventing network-level surveillance of which HTTPS sites a user visits. Nginx 1.30 adds ECH support, making it possible for privacy-conscious WordPress operators to offer this to their users.
Multipath TCP
Multipath TCP (MPTCP) allows establishing connections using multiple network paths simultaneously, improving resilience and throughput for mobile users who switch between Wi-Fi and cellular. Nginx 1.30 enables MPTCP support. Most useful for sites with significant mobile traffic.
103 Early Hints support
HTTP 103 Early Hints allows the server to send preload hints for critical resources before the full response is ready. This can improve LCP by giving browsers a head start on fetching CSS, fonts, and LCP images. WordPress 6.8's Speculative Loading features work well with Nginx Early Hints.
Summary
A well-configured Nginx server is the foundation of a fast, secure WordPress deployment. The core elements – correct PHP-FPM passthrough with Unix sockets, FastCGI full-page caching for anonymous traffic, security headers applied globally, and rate limiting on the login page – together produce a server that handles traffic efficiently, passes all Core Web Vitals, and resists the automated attack traffic that every public WordPress site faces. The configuration is not complicated, but the details matter: the wrong bypass rule in FastCGI cache can serve stale cart pages to shoppers, and a missing try_files check before PHP passthrough can enable code execution vulnerabilities.


