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.

Event-driven modelNginx uses an asynchronous, event-driven architecture. A single worker process handles thousands of connections simultaneously using non-blocking I/O. When a response is waiting for PHP-FPM or the database, the worker handles other connections instead of blocking. This is why Nginx stays responsive under traffic spikes that would exhaust Apache's process pool.
No .htaccessUnlike Apache, Nginx does not support per-directory .htaccess files. All configuration lives in server block files, which Nginx reads once at startup (or on reload). This is both a limitation (WordPress cannot manage its own rewrites without server access) and a performance advantage (no disk I/O to check for .htaccess files on every request).
WordPress rewrite ruleWordPress requires a catch-all rewrite that sends non-existent files to 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.
Static filesNginx serves static files (images, CSS, JavaScript) from disk with minimal overhead – no PHP involvement. Set 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 directiveslisten 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 configurationUse TLS 1.2 and 1.3 only: 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 passthroughPass .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.
Blocking sensitive filesBlock direct access to WordPress configuration and sensitive files: 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.

Cache configuration

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.

Cache bypass

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;.

Cache validity

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.

Cache purging

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.

Strict-Transport-Securityadd_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.
X-Content-Type-Optionsadd_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.
X-Frame-Optionsadd_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.
Referrer-Policyadd_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.
Permissions-Policyadd_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.

Define the zoneIn 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.
Apply to wp-login.phpIn your server block: 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.
XML-RPC blockingBlock xmlrpc.php entirely if you do not use Jetpack or other XML-RPC-dependent integrations: 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).
Admin area restrictionIf all administrators access the site from known IP ranges, restrict wp-admin and wp-login.php to those IPs: 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:

HTTP/2 to upstreams

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.

ECH

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.

MPTCP

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.

Early Hints

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.

Need Help with WordPress?

If you have questions after reading the articles or need support – we are at your disposal. No commitments, no marketing jargon – a concrete proposal after a brief conversation.

Phone

+48 608 271 665

Mon–Fri, 8:00–16:00 CET

E-mail

contact@weboptimo.pl

We respond within 24h

Company

WebOptimo

VAT ID: PL6391758393