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.
Steps to disable PHP functions in PHP-FPM:
- Check which base PHP-FPM configuration file the selected runtime loads and read the current disable_functions value.
$ 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
- Search the PHP-FPM pool directory for pool-level disable_functions appends before editing the main file.
$ 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.
- Create a backup of the active PHP-FPM configuration file before editing it.
$ 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.
- Open the active PHP-FPM configuration file in a text editor.
$ sudoedit /etc/php/8.5/fpm/php.ini
- 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 same PHP-FPM configuration tree before reloading the service.
$ 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.
- Reload the PHP-FPM service so new worker processes pick up the updated directive.
$ 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.
- Create a temporary probe in the same document root or virtual host that serves the application.
<?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.
- Request the temporary probe through the web server.
$ 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
- Remove the temporary probe after the check.
$ 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.
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.