PHP-FPM for WordPress – Pool Configuration, OPcache, and Security

Published: April 14, 2026 · Author: Marcin Szewczyk-Wilgan

PHP-FPM (FastCGI Process Manager) is the PHP execution environment behind every well-configured WordPress server. Understanding how it works – and how to configure it correctly – makes the difference between a server that handles traffic spikes gracefully and one that crashes under load. This article covers the complete PHP-FPM setup for WordPress: pool configuration, process management modes, Unix socket vs TCP, OPcache and JIT, Redis object cache, and security hardening with open_basedir and disable_functions.

How PHP-FPM Works

PHP-FPM manages a pool of PHP worker processes. When Nginx or Apache receives a PHP request, it hands it off to PHP-FPM via the FastCGI protocol. PHP-FPM assigns the request to an available worker, which executes the PHP code and returns the result. The web server then sends the response to the client.

WorkersEach PHP-FPM worker is a separate process that handles one request at a time. Workers are pre-forked from a master process and persist between requests (unlike mod_php, which creates a new process per request). The number of active workers directly determines how many simultaneous PHP requests your server can handle.
PoolsPHP-FPM can run multiple pools simultaneously, each with its own worker configuration, user/group, Unix socket, and resource limits. Separate pools per site provide process isolation: a WordPress installation running as user www-wp1 cannot access files owned by www-wp2, even if a vulnerability is exploited.
FastCGI protocolNginx communicates with PHP-FPM via FastCGI. The fastcgi_pass directive in Nginx points to either a Unix socket (unix:/var/run/php-fpm/wordpress.sock) or a TCP address (127.0.0.1:9000). Unix sockets are faster for same-server setups; TCP is required when PHP-FPM runs on a separate host.

Calculating pm.max_children

The most critical PHP-FPM configuration parameter is pm.max_children – the maximum number of PHP worker processes. Set it too low and requests queue or timeout under load; set it too high and workers compete for RAM, triggering swapping and catastrophic performance degradation.

The formula(Available RAM − OS overhead − MySQL RAM − web server RAM) ÷ average PHP worker size × 0.85
The 0.85 factor provides a safety margin so you never push the server to exactly its memory limit under burst load.
Measuring worker sizeThe average PHP-FPM worker size for WordPress ranges from 30 to 80 MB depending on your plugin set. Measure it on your actual installation: ps aux --sort=-%mem | grep php-fpm | awk '{print $6}' gives RSS in KB. Divide by 1024 for MB. Average 5–10 readings after normal use.
Example: 2 GB VPSServer RAM: 2048 MB. OS overhead: ~256 MB. MySQL: ~256 MB. Nginx: ~64 MB. Available for PHP: ~1472 MB. Average worker: 45 MB. Maximum workers: 1472 ÷ 45 × 0.85 ≈ 27. Set pm.max_children = 27.
Example: 4 GB VPSAvailable for PHP: ~3200 MB. Average worker: 50 MB. Maximum workers: 3200 ÷ 50 × 0.85 ≈ 54. A 4 GB server running an optimized WordPress stack can typically handle 50–60 concurrent PHP requests comfortably.
pm.max_requestsSet pm.max_requests = 500 to recycle workers after 500 requests. This prevents memory leaks from accumulating indefinitely. A worker is gracefully replaced by a fresh one after hitting the limit. Values between 200 and 1000 are reasonable; lower values catch leaks faster at the cost of slightly more spawn overhead.

Process Management Modes

PHP-FPM offers three process management modes. The right choice depends on your traffic pattern and available memory.

dynamic

Variable traffic – the default

Starts with pm.start_servers workers (e.g., 5), maintains at least pm.min_spare_servers idle workers, and spawns more up to pm.max_children when load increases. Idle workers above pm.max_spare_servers are gracefully terminated. Ideal for sites with variable traffic patterns.

static

Consistent high traffic

Keeps exactly pm.max_children workers running at all times, regardless of load. Eliminates spawn latency under traffic spikes. Requires more RAM when idle but is the best choice for high-traffic sites where spawn overhead would be noticeable. Set pm.max_children to a value your server can sustain continuously.

ondemand

Low-traffic or idle sites

Starts zero workers and spawns them only when a request arrives. Workers are killed after pm.process_idle_timeout (default 10 seconds) of inactivity. Saves memory on servers hosting many low-traffic sites. The spawn latency on first request is noticeable, making this inappropriate for sites with consistent traffic.

Slowlog

Performance diagnostics

Enable slowlog = /var/log/php-fpm/wordpress-slow.log and request_slowlog_timeout = 10s to capture stack traces of requests that take longer than 10 seconds. This is invaluable for diagnosing performance issues caused by specific plugins, database queries, or external API calls.

Unix Socket vs TCP

When Nginx and PHP-FPM run on the same server, Unix sockets are always preferable to TCP connections on the loopback interface.

Performance differenceUnix sockets bypass the TCP/IP stack entirely, communicating directly through the kernel. Benchmarks consistently show 5–10% lower latency and better throughput for same-server setups. Under high concurrency (hundreds of requests per second), the difference becomes more significant because TCP's overhead compounds.
Nginx configurationfastcgi_pass unix:/var/run/php/php8.3-fpm-wordpress.sock;
The socket file path must match the listen directive in your PHP-FPM pool configuration: listen = /var/run/php/php8.3-fpm-wordpress.sock.
PermissionsSet listen.owner = www-data, listen.group = www-data, and listen.mode = 0660 in the pool configuration. Nginx runs as www-data and must have read/write access to the socket file. Incorrect permissions cause connect() to unix:/...sock failed (13: Permission denied) errors.
When to use TCPUse TCP (127.0.0.1:9000 or a specific port) when PHP-FPM runs on a separate server from the web server, or when load balancing across multiple PHP-FPM hosts. TCP is the only option when the connection crosses a network boundary.

OPcache and JIT

OPcache is the single most impactful PHP performance optimization for WordPress. JIT (introduced in PHP 8.0) provides further gains for specific workloads.

OPcachePHP normally parses and compiles every script on each request. OPcache stores compiled bytecode in shared memory so compilation happens once. For WordPress, this means every request avoids parsing WordPress core, your theme, and all active plugins from scratch. The speedup is consistently 50–70% on first-byte response time.
Key OPcache settingsopcache.memory_consumption = 256 (MB of shared memory for cached bytecode), opcache.max_accelerated_files = 20000 (WordPress + plugins can easily have 10,000+ PHP files), opcache.validate_timestamps = 0 (disable file change checks in production for best performance – reload PHP-FPM after deployments to clear cache), opcache.revalidate_freq = 60 (seconds between file checks if validate_timestamps is 1).
JITJIT compiles frequently executed bytecode to native machine code at runtime. For typical WordPress workloads (database-heavy, I/O-bound), JIT provides modest improvements of 5–15%. The gains are more pronounced for CPU-intensive code: image processing, cryptographic operations, complex calculations. Enable with opcache.jit = tracing and opcache.jit_buffer_size = 64M.
Redis object cacheOPcache accelerates PHP execution. Redis object cache reduces database queries. The WordPress object cache (enabled with a drop-in plugin like Redis Object Cache by Till Krüss) stores frequently accessed database results in Redis. For sites with heavy database loads, the combination of OPcache + Redis produces far better results than either alone.

Security Hardening

PHP-FPM configuration is also where security isolation is enforced. These settings limit what PHP can do if a vulnerability is exploited.

open_basedir

Restricting file system access

Set php_admin_value[open_basedir] = /var/www/site:/tmp:/usr/share/php to prevent PHP from reading files outside the WordPress installation. If a vulnerability is exploited and an attacker runs PHP code, they cannot read /etc/passwd, access other sites' files, or read credentials from outside the allowed paths.

disable_functions

Blocking dangerous functions

Disable PHP functions that WordPress does not need but attackers do: php_admin_value[disable_functions] = exec,passthru,shell_exec,system,proc_open,popen,pcntl_exec. These functions allow PHP to execute system commands. Legitimate WordPress plugins do not need them; most malware does.

Process isolation

Separate pools per site

Run each WordPress site under a dedicated Unix user with its own PHP-FPM pool. Set user = www-wp-site1 and group = www-wp-site1. This ensures that code running under site1's PHP process cannot read or write files owned by site2. Critical for shared hosting and agency multi-site servers.

Resource limits

Preventing runaway processes

Set php_admin_value[memory_limit] = 256M to cap memory per request, and configure request_terminate_timeout = 60s to kill requests that run longer than one minute. These limits prevent a single runaway plugin or crashed script from consuming all server resources.

Summary

Correctly configured PHP-FPM is one of the highest-leverage infrastructure improvements for a WordPress server. The combination of properly calculated pm.max_children, Unix socket communication, OPcache with adequate shared memory, and Redis object cache typically reduces server response times by 60–80% compared to a default PHP-FPM installation. Security hardening with open_basedir, disable_functions, and per-site process isolation significantly reduces the blast radius of any plugin vulnerability that gets exploited. These are not optional extras for production servers – they are the baseline configuration.

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