Disabling selected PHP functions limits what a compromised application, plugin, or uploaded script can do after code reaches the interpreter. On single-purpose sites or shared runtimes that never need shell access, blocking helpers such as exec(), shell_exec(), system(), passthru(), proc_open(), and popen() reduces the blast radius of remote-code-execution bugs and weak admin tooling.
The control point is the disable_functions directive, a comma-delimited list of internal function names resolved when the PHP SAPI starts. Current PHP documentation marks it as INI_SYSTEM, requires it in php.ini, and notes that PHP 8+ removes disabled internal functions from the function table instead of leaving them callable with the older disabled warning.
This is a hardening control rather than a security boundary, so keep the list narrow and test the application after reloading the runtime because a broad block list can break queue workers, deployment hooks, mail delivery, or image processing that legitimately shells out. The steps below stay on the packaged Ubuntu or Debian PHP-FPM layout under /etc/php/<version>/fpm/, and pool-level php_admin_value[disable_functions] or php_value[disable_functions] settings must be checked before editing because current PHP-FPM behavior appends those values rather than replacing the base php.ini list.
Steps to disable PHP functions in PHP-FPM:
- Check which PHP-FPM php.ini file the target runtime resolves and note the current disable_functions value.
$ sudo php-fpm8.3 -y /etc/php/8.3/fpm/php-fpm.conf -i | grep -E '^Loaded Configuration File =>|^disable_functions' Loaded Configuration File => /etc/php/8.3/fpm/php.ini disable_functions => no value => no value
Replace 8.3 with the installed PHP major.minor version. On hosts that expose only an unversioned binary, use sudo php-fpm -y /etc/php/<version>/fpm/php-fpm.conf -i instead. This confirms the base php.ini tree for that PHP-FPM binary; pool-level appends still need the next-step pool review or a short-lived probe through the same request path. php --ini reports the CLI tree, which can differ from the web-facing PHP-FPM runtime.
Related: How to find PHP configuration files
- Check the PHP-FPM pool directory for appended disable_functions values before editing the main file.
$ sudo grep -REn 'php_(admin_)?value\[disable_functions\]' /etc/php/8.3/fpm/pool.d /etc/php-fpm.d 2>/dev/null /etc/php/8.3/fpm/pool.d/customer-portal.conf:23:php_admin_value[disable_functions] = show_source
No output means the sampled pool files are not currently appending extra disabled functions. Current PHP-FPM documentation notes that pool settings defining disable_functions append to the base value from php.ini instead of replacing it.
- Create a backup of the active PHP-FPM configuration file before editing it.
$ sudo cp /etc/php/8.3/fpm/php.ini /etc/php/8.3/fpm/php.ini.bak-$(date +%Y%m%d%H%M%S)
A malformed php.ini can stop new worker processes from loading cleanly, so keep the timestamped backup until the new list is confirmed.
- Open the active PHP-FPM configuration file in a text editor.
$ sudoedit /etc/php/8.3/fpm/php.ini
Related: How to find PHP configuration files
- Set disable_functions to the internal functions that should be unavailable to the runtime.
disable_functions = exec,shell_exec,system,passthru,proc_open,popen
Extend an existing list instead of replacing it blindly when the file already disables other functions. Only internal functions are affected by this directive.
Keep the list tight. Functions such as putenv, mail, or proc_open are common breakpoints for frameworks, job runners, deployment tooling, and integrations that shell out to other programs.
- Test the PHP-FPM configuration after editing the file.
$ sudo php-fpm8.3 -y /etc/php/8.3/fpm/php-fpm.conf -t [26-Mar-2026 06:12:41] NOTICE: configuration file /etc/php/8.3/fpm/php-fpm.conf test is successful
Use the matching binary for the installed package, such as php-fpm on RHEL-family hosts, when the service is not versioned.
Do not reload the service until the configuration test succeeds.
- Reload the PHP-FPM service so new worker processes pick up the updated directive.
$ sudo systemctl reload php8.3-fpm
Packaged Ubuntu and Debian builds commonly use a versioned unit such as php8.3-fpm, while other layouts can expose a generic unit such as php-fpm. Reload Apache instead when the application runs through the Apache module instead of PHP-FPM.
- Verify that the resolved PHP-FPM runtime now reports the disabled list.
$ sudo php-fpm8.3 -y /etc/php/8.3/fpm/php-fpm.conf -i | grep -E '^Loaded Configuration File =>|^disable_functions' Loaded Configuration File => /etc/php/8.3/fpm/php.ini disable_functions => exec,shell_exec,system,passthru,proc_open,popen => exec,shell_exec,system,passthru,proc_open,popen
This confirms the startup value from the base php.ini tree for the chosen PHP-FPM binary. When the application is served through a pool, virtual host, or different SAPI that can append or diverge from that base value, confirm the same request path with a short-lived probe instead of trusting CLI output alone.
Related: How to show disabled PHP functions
Mohd Shakir Zakaria is a cloud architect with deep roots in software development and open-source advocacy. Certified in AWS, Red Hat, VMware, ITIL, and Linux, he specializes in designing and managing robust cloud and on-premises infrastructures.
