Disabling selected PHP functions limits what application code can do when a site, pool, or tenant never needs shell access or child-process execution. Blocking internal helpers such as exec(), shell_exec(), system(), passthru(), proc_open(), and popen() reduces the damage a compromised plugin, uploaded script, or exposed admin tool can do after reaching the runtime.
The control point is the disable_functions directive, a comma-delimited list that PHP resolves when the runtime starts. Current PHP documentation marks it as INI_SYSTEM, only internal functions are affected, and 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 sandbox, so keep the list narrow and confirm the served runtime instead of trusting CLI alone. The steps below use the packaged Ubuntu and Debian PHP-FPM layout under /etc/php/<version>/fpm/, where pool files can append extra disabled functions with php_value[disable_functions] or php_admin_value[disable_functions] and broad blocks such as mail, putenv, or proc_open can break queue workers, deployment hooks, or integrations that legitimately shell out.
$ sudo php-fpm8.5 -y /etc/php/8.5/fpm/php-fpm.conf -i phpinfo() PHP Version => 8.5.4 ##### snipped ##### Loaded Configuration File => /etc/php/8.5/fpm/php.ini ##### snipped ##### Scan this dir for additional .ini files => /etc/php/8.5/fpm/conf.d ##### snipped ##### disable_functions => no value => no value
Replace 8.5 with the installed PHP major.minor version when the host packages another branch. On hosts that ship an unversioned binary, use sudo php-fpm -y /etc/php/<version>/fpm/php-fpm.conf -i instead. php --ini reports the CLI tree, which can differ from the web-facing PHP-FPM runtime.
Related: How to find PHP configuration files
$ sudo grep -RF "value[disable_functions]" /etc/php/8.5/fpm/pool.d/
No output means the sampled pool files are not currently adding extra disabled functions. In PHP-FPM, php_value[disable_functions] and php_admin_value[disable_functions] append to the base php.ini list instead of replacing it, so a served pool can end up with a broader effective list than the base value shown in the first step.
$ sudo cp /etc/php/8.5/fpm/php.ini /etc/php/8.5/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.
$ sudoedit /etc/php/8.5/fpm/php.ini
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.
$ sudo php-fpm8.5 -y /etc/php/8.5/fpm/php-fpm.conf -t [05-Jun-2026 21:28:46] NOTICE: configuration file /etc/php/8.5/fpm/php-fpm.conf test is successful
Use the matching binary for the installed package, such as php-fpm on hosts that do not ship a versioned PHP-FPM command.
Do not reload the service until the configuration test succeeds.
$ sudo systemctl reload php8.5-fpm
Packaged Ubuntu and Debian systems commonly use a versioned unit such as php8.5-fpm, while other layouts can expose an unversioned unit such as php-fpm. Reload Apache instead when the application runs through the Apache module instead of PHP-FPM.
<?php header('Content-Type: text/plain'); echo 'disable_functions=', ini_get('disable_functions') ?: 'no value', "\n"; echo 'exec_exists=', function_exists('exec') ? 'yes' : 'no', "\n"; ?>
Save the file as php-disable-functions-check.php in the exact site, pool, or virtual host that should receive the restricted runtime.
$ curl -sS https://app.example.com/php-disable-functions-check.php disable_functions=exec,shell_exec,system,passthru,proc_open,popen exec_exists=no
exec_exists=no confirms that the served PHP-FPM runtime no longer has exec() in the function table. The reported disable_functions value also confirms that the request path is using the expected php.ini and pool settings.
Related: How to show disabled PHP functions
$ sudo rm /var/www/app.example.com/public/php-disable-functions-check.php
Leaving the file accessible exposes runtime policy details that should not remain public.