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:
- Master process: reads the configuration, opens the sockets and starts or ends workers. It does not execute application code itself.
- 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.
- 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.
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:
- Install the update: PHP 8.5.12, 8.4.27, 8.3.36 or 8.2.35, or a confirmed distribution backport.
- 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.
- Set interim values as locked: The php.net measures, such as
session.upload_progress.enabled=0, belong in the pool viaphp_admin_valueorphp_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.