In short: With PHP disable_functions you block functions through which PHP executes system commands, such as exec, shell_exec or proc_open. This makes the next step harder for attackers after a break-in. The setting works only from php.ini or the FPM pool, must suit the application and does not replace an update.
Why disable_functions makes sense right now
PHP2Shell is about remote code execution without authentication. The two vulnerabilities CVE-2026-109618 and CVE-2026-109619 sit in the PHP interpreter itself and are fixed in PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35. The update is the only way to close them.
Hardening starts one layer later. Anyone who can execute code on a server once usually wants to go further: start system commands, download tools, set up a permanent backdoor. Many of these steps run through a few PHP functions. If they are blocked, the path becomes more laborious and more conspicuous.
disable_functions is therefore no substitute for patches but a building block for the case that something gets through despite all precautions. That applies to future vulnerabilities just as much as to PHP2Shell.
What PHP disable_functions achieves and what it does not
The setting removes the named functions from the runtime environment. If a script calls a blocked function, PHP aborts with an error. The block applies to any code running in this PHP process, including planted code.
You should know the limits:
- The setting is not a sealed protective space. PHP extensions, flaws in the interpreter or programs outside PHP can bypass it.
- It does not prevent an attacker from reading or writing files that the PHP user has access to. File permissions and isolation are responsible for that.
- It works only from php.ini or as
php_admin_value. It cannot be set withini_set(),.user.inior.htaccess.
disable_functions brings the most together with a clean php.ini, separate users per website and restrictive file permissions. The basics are in the guide Configuring php.ini securely.
Which functions you should block
For typical websites with no need for system commands, the following list has proved its worth. It covers functions for starting processes and controlling running processes.
| Group | Functions | Typical legitimate use |
|---|---|---|
| Executing commands | exec, shell_exec, system, passthru |
Image processing, backups via script |
| Opening processes | proc_open, popen |
Composer, PDF tools, mail delivery |
| Controlling processes | proc_nice, proc_terminate, proc_get_status |
Queues, background jobs |
| Process management | pcntl_exec, pcntl_fork |
Mostly only on the command line |
| Signals and permissions | posix_kill, posix_setuid |
Rarely in web operation |
As a configuration it looks like this:
; php.ini of the web operating mode, e.g. /etc/php/8.3/fpm/php.ini
disable_functions = exec,shell_exec,system,passthru,proc_open,popen,proc_nice,proc_terminate,proc_get_status,pcntl_exec,pcntl_fork,posix_kill,posix_setuid
If the list does not apply to all websites on the server, set it per FPM pool. After reloading, check which list actually applies in that pool:
; /etc/php/8.3/fpm/pool.d/meine-seite.conf (Auszug)
php_admin_value[disable_functions] = exec,shell_exec,system,passthru,proc_open,popen
Test the application beforehand: how to avoid outages
Anyone who sets the list unchecked risks that image resizing, PDF invoices or mail delivery suddenly stop working. So search the code beforehand for the affected functions:
# Where and how often are the functions used in the code?
grep -rnoE "\b(exec|shell_exec|system|passthru|proc_open|popen|pcntl_exec)\s*\(" /var/www/meine-seite --include="*.php" \
| cut -d: -f1 | sort | uniq -c | sort -rn | head -30
Hits do not automatically mean the function is needed in operation. Many libraries first check whether a function is available and otherwise fall back. What matters is a test in a staging environment covering all important processes: ordering, invoicing, form delivery, image upload, scheduled tasks.
If an application strictly requires one of these functions, check the vendor's system requirements on its official site. Often the task can be moved to a separate pool or a command-line script that is not reachable via the web.
Enabling the setting and checking its effect
After the change, PHP must reread the configuration. Then check the value in your website's operating mode, not on the command line:
sudo systemctl reload php8.3-fpm
php-fpm8.3 -i | grep "^disable_functions"
# Check the error log for calls of blocked functions
sudo grep -i "has been disabled" /var/log/php/*.log | tail -20
The messages in the error log are valuable later on as well. If calls of blocked functions suddenly appear there although your application does not use them, that may point to planted code. How to proceed then is described in the guide Detecting and removing webshells.
Typical mistakes when using disable_functions
In practice, hardening rarely fails because of the technology but because of small things. These mistakes are particularly common:
- Wrong file changed: The list is only in the command line's php.ini; the website runs through PHP-FPM and stays unprotected.
- No restart: The file is correct, but the running processes still work with the old configuration.
- Blocked too broadly: Functions such as
ini_setormailare blocked too, and important processes fail unnoticed. Errors then often show up only days later, for example when order confirmations stop arriving. - Set once, never checked: After a move, a change of PHP version or a reinstallation via the hosting panel, the list has vanished.
- A false sense of security: The block is understood as a replacement for updates. It does not close a vulnerability in the interpreter, though.
So include checking the active value in your routine after every PHP update. A quick look is enough, costs hardly any time and prevents a hardening measure from being silently lost.
With several websites on one server: create a separate pool with its own list for each website. In addition, open_basedir limits the directories PHP may access.
How does PHP SOS help?
- We check your application's code and processes for the use of critical functions.
- We define a suitable block list for each website and test it before going live.
- We combine the block with pools, file permissions and logging into a coherent hardening concept.
- We document the settings and monitor the error log for conspicuous calls.
How this works as a complete package is described in the service Server hardening for PHP web servers.
Frequently asked questions
Can I set disable_functions via ini_set() or .htaccess?
No. The setting works only from php.ini or as php_admin_value in the FPM pool or the Apache configuration. It cannot be set at runtime, and the application cannot re-enable blocked functions either.
Does disable_functions protect against PHP2Shell?
No. PHP2Shell affects the PHP interpreter itself; the vulnerability is closed only by the update. Blocked functions can, however, limit an attacker's room for manoeuvre after a successful exploitation.
Why does php -i show a different list than my website?
The command line often reads its own php.ini. For the website, the configuration of PHP-FPM, mod_php or LiteSpeed counts. Therefore check the value in the operating mode through which your website runs.
Which applications need proc_open or exec?
That depends on the application. Typical are image processing via external programs, PDF generation, mail delivery via sendmail or tools such as Composer. Check this in the code and in the vendor's system requirements.