Current situation: PHP2Shell. Two critical PHP vulnerabilities (CVSS 9.2) allow malicious code without login. Patches since 21 October 2026.What to do now →

PHP-FPM Explained: Process Manager, Pools and Workers

PHP-FPM runs PHP as its own service with pools and workers. How the process model works, which directives matter and why workers must restart after updates.

Last updated 5 min readPHP knowledge

In short: PHP-FPM (FastCGI Process Manager) runs PHP as its own service alongside the web server. A master process manages pools, and each pool starts workers that handle requests over FastCGI. Pools can be operated per website with their own user, their own limits and locked PHP settings. Because workers handle many requests one after another, they must all be restarted after a PHP update.

What is PHP-FPM?

The PHP manual describes FPM as PHP's primary FastCGI implementation, with features that are mainly useful for heavily loaded websites. FPM was included with PHP 5.3.3 as its own SAPI, that is, its own operating mode. Its SAPI name is fpm-fcgi.

The difference from mod_php: PHP does not run inside the web server but as its own service. Nginx, Apache or another web server passes PHP requests on over the FastCGI protocol and receives the finished response back. Web server and PHP can thereby be restarted, configured and secured independently of each other. How FastCGI works in principle is explained under CGI and FastCGI.

Master, pools and workers: the process model

PHP-FPM consists of three levels:

  1. Master process: reads the configuration, opens the sockets and starts or ends workers. It does not execute application code itself.
  2. Pools: Each pool has a name, its own socket or port, its own Unix user and its own PHP settings. The manual explicitly mentions different uid, gid, chroot, environment and php.ini per pool.
  3. Workers: processes of a pool that accept and execute requests. A worker handles many requests one after another before it is replaced.

How many workers run is determined by the process manager with the pm directive:

Mode Behaviour Important directives
static fixed number of workers pm.max_children
dynamic workers between a lower and an upper limit pm.start_servers, pm.min_spare_servers, pm.max_spare_servers
ondemand workers only when needed, removed after idle time pm.process_idle_timeout (default 10s)

In all modes, pm.max_children limits the number of simultaneous requests. pm.max_requests replaces a worker after a fixed number of requests; the default 0 means unlimited.

Which mode fits depends on the load. static always keeps the same number of workers ready and suits evenly and heavily used applications with enough memory. dynamic is a compromise for most websites. ondemand saves memory on servers with many small websites, because pools without visitors occupy no workers; in return, the first request after a pause takes a little longer. A pm.max_children set too high can drive the server into a memory shortage during load peaks, while one set too low leads to waiting times and error messages from the web server.

A pool with secure basic settings

An example of a separate pool per website, with directives from the PHP manual. Paths and values are examples:

; /etc/php/8.3/fpm/pool.d/kunde-a.conf
[kunde-a]
user = kunde-a
group = kunde-a

listen = /run/php/kunde-a.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

pm = ondemand
pm.max_children = 10
pm.process_idle_timeout = 10s

request_terminate_timeout = 60s
security.limit_extensions = .php
clear_env = yes

php_admin_value[open_basedir] = /var/www/kunde-a:/tmp
php_admin_flag[session.upload_progress.enabled] = off
php_admin_value[error_log] = /var/log/php/kunde-a.error.log

According to the manual, values set via php_value and php_flag can be changed at runtime with ini_set(), whereas values set via php_admin_value and php_admin_flag cannot. security.limit_extensions defaults to .php .phar and determines which file extensions FPM executes as the main script. clear_env = yes is the default and clears the workers' environment.

For monitoring there is a status page via pm.status_path and a ping page via ping.path. Both should be reachable internally only. FPM logs slow requests with a backtrace if request_slowlog_timeout and slowlog are set.

Important

The PHP manual warns: anyone who can open a FastCGI connection to PHP-FPM controls the configuration of the request and can thereby execute arbitrary code. PHP-FPM must never be reachable from an untrusted network. Use a Unix socket or listen.allowed_clients.

What role does PHP-FPM play in PHP2Shell?

PHP-FPM is among the affected operating modes. The process model becomes particularly clear with CVE-2026-109619: internal state belonging to a request can persist after its memory has been released, and a later request in the same worker can use it before it is reset. This is only possible because workers handle many requests one after another.

Three obligations follow:

  1. Install the update: PHP 8.5.12, 8.4.27, 8.3.36 or 8.2.35, or a confirmed distribution backport.
  2. Restart all workers: New files on disk change nothing as long as old workers are running. This applies to every installed FPM version and every pool. The commands are under Restarting PHP-FPM and all workers.
  3. Set interim values as locked: The php.net measures, such as session.upload_progress.enabled=0, belong in the pool via php_admin_value or php_admin_flag, not in .user.ini.

Separation into pools does not replace an update but limits the damage: if each website runs under its own user, an attacker does not automatically reach the other sites' files after a break-in. What this looks like in practice is described in Isolating PHP-FPM pools per website; the integration in Nginx is covered under Securing Nginx with PHP-FPM.

How does PHP SOS help?

  • Record pools: We list all FPM versions, pools, users and sockets on your server.
  • Patch and restart: We update PHP and use start times to verify that no old worker is still running.
  • Separate pools: As part of server hardening we set up one pool per website with its own user and locked settings.
  • Check reachability: We verify that FastCGI sockets and status pages are not reachable from outside.

Frequently asked questions

What does FPM stand for?

FPM stands for FastCGI Process Manager. The PHP manual describes it as PHP's primary FastCGI implementation, with features that are mainly useful for heavily loaded websites. It was included with PHP 5.3.3.

What is the difference between a pool and a worker?

A pool is a group of worker processes with a shared configuration: user, socket, limits and PHP settings. A worker is a single process that handles requests one after another. A server can have many pools, for example one per website.

Should PHP-FPM be reachable over TCP or over a Unix socket?

If the web server runs on the same host, the PHP manual recommends a Unix socket. With TCP, listen.allowed_clients must be set, because otherwise any address may connect. According to the manual, an openly reachable FastCGI port allows arbitrary code execution.

Does pm.max_requests protect against flaws such as PHP2Shell?

No. pm.max_requests replaces a worker after a fixed number of requests and mainly helps against memory leaks in extensions. Against a flaw in the interpreter, only the update followed by a restart of all workers helps.

Sources

Immediate contact

Every hour counts. Write to us now.

Briefly describe your system, PHP version and observation. We will get back to you, clarify the situation and work remotely until the incident is closed and documented.

Immediate help