In short: To configure Apache for PHP securely, connect PHP via PHP-FPM and mod_proxy_fcgi, switch off .htaccess with AllowOverride None and set ServerTokens Prod, ServerSignature Off, TraceEnable Off and Options -Indexes. Adapt LimitRequestBody to the PHP limits and disable modules you do not need. The configuration does not replace a PHP update but noticeably reduces the attack surface.
Apache after PHP2Shell: more than a restart
In October 2026, Apache operators were under particular pressure. The PHP team rates the risk highest for mod_php because an exploit was demonstrated there before any application code even runs. The acute steps, that is, patching and a complete restart, are described on the page Why mod_php carries the highest risk.
After the patch, the question remains how good the Apache configuration itself is. Grown servers often carry settings from many years: directory listings, open .htaccess permissions, modules nobody needs any more. Each of these can make a later mistake bigger than it needed to be.
This article describes a lean, secure basic configuration for Apache 2.4 with PHP-FPM. The examples use Debian and Ubuntu paths; on the RHEL family the files are under /etc/httpd/.
Connecting PHP via PHP-FPM instead of mod_php
With PHP-FPM, PHP runs in its own processes, separate from the web server. Apache passes requests on via mod_proxy_fcgi. According to the Apache documentation, connecting via SetHandler has an advantage over ProxyPassMatch: Apache first maps the request to a file in the file system itself and only then hands it to PHP-FPM.
<FilesMatch "\.php$">
SetHandler "proxy:unix:/run/php/seite1.sock|fcgi://localhost/"
</FilesMatch>
On Debian and Ubuntu you enable the modules with a2enmod proxy_fcgi. Anyone switching from mod_php disables the PHP module and then moves from the prefork to the event MPM. Background on the old operating mode is explained in mod_php explained: PHP inside the Apache process.
On the PHP-FPM side, security.limit_extensions = .php belongs in every pool. According to the PHP manual, FPM otherwise also executes .phar files.
Configuring Apache for PHP securely: the core options
Some settings apply to the whole server and belong in a separate configuration file, for example /etc/apache2/conf-available/haertung.conf, enabled with a2enconf haertung.
# Shorten version information (possible server-wide only)
ServerTokens Prod
ServerSignature Off
# Disable the TRACE method
TraceEnable Off
# Release nothing by default, allow websites selectively
<Directory />
Options None
AllowOverride None
Require all denied
</Directory>
The following table classifies the most important options. Defaults and effects come from the Apache documentation.
| Directive | Default | Recommendation | Why |
|---|---|---|---|
ServerTokens |
Full |
Prod |
Server header states only “Apache” |
ServerSignature |
Off |
Off |
no version line below error pages |
TraceEnable |
on |
Off |
TRACE is rejected with 405 |
Options |
FollowSymlinks |
-Indexes, symlinks only by owner |
no directory listings |
AllowOverride |
None |
None |
.htaccess is not evaluated |
LimitRequestBody |
1 GiB (since 2.4.54) | matching post_max_size |
reject large requests early |
Important with AllowOverride: many distributions and CMS guides set AllowOverride All for the web root. With it, any .htaccess file can change rewrite rules, handlers and, with mod_php, even PHP settings. An attacker with write permissions in an upload folder can place their own .htaccess there. With AllowOverride None this path is closed.
A VirtualHost as a template
The following VirtualHost combines the points for a single website. Adapt certificate paths, socket and directories. mod_ssl and mod_headers must be active.
<VirtualHost *:443>
ServerName www.example.de
DocumentRoot /var/www/seite1/public
SSLEngine on
SSLCertificateFile /etc/ssl/seite1/fullchain.pem
SSLCertificateKeyFile /etc/ssl/seite1/privkey.pem
<Directory /var/www/seite1/public>
Options -Indexes +SymLinksIfOwnerMatch
AllowOverride None
Require all granted
</Directory>
<FilesMatch "\.php$">
SetHandler "proxy:unix:/run/php/seite1.sock|fcgi://localhost/"
</FilesMatch>
# Block version control and environment files
<DirectoryMatch "/\.(git|svn|hg)">
Require all denied
</DirectoryMatch>
<FilesMatch "^\.env">
Require all denied
</FilesMatch>
# 32 MiB, matching post_max_size = 32M
LimitRequestBody 33554432
Header unset X-Powered-By
Header always unset X-Powered-By
</VirtualHost>
The double Header unset is not an oversight. According to the Apache documentation, headers from mod_proxy_fcgi end up in a different internal table than headers of the server itself; with both variants you catch both cases. Further headers that actively protect visitors are described in the guide Security headers for websites.
Upload and cache folders additionally need an execution block. How to find and block all writable folders is covered under Forbidding PHP execution in upload folders.
Pruning modules and checking the result
Every loaded module is code that responds to requests. Check which modules are active and disable what you do not need. Typical candidates are autoindex, status without access restriction, cgi and userdir.
# Show active modules
apachectl -M | sort
# Example Debian/Ubuntu: disable the directory listing module
sudo a2dismod autoindex
# Check the configuration and only then reload
sudo apachectl configtest && sudo systemctl reload apache2
# Test from outside: TRACE should return 405, .git 403
curl -s -o /dev/null -w "%{http_code}\n" -X TRACE https://www.example.de/
curl -s -o /dev/null -w "%{http_code}\n" https://www.example.de/.git/HEAD
mod_status deserves special attention. The status page shows running requests with addresses and requested paths. If it is needed, restrict access with Require ip to your management networks. The same applies to PHP-FPM status pages made reachable through Apache.
Do not change AllowOverride without preparation. First transfer the rules from all .htaccess files into the VirtualHost and test the website on a copy. Otherwise redirects and pretty URLs stop working after the reload.
As an additional layer in front of the application, a dedicated web application firewall can make sense; the introduction is in Using ModSecurity and the OWASP Core Rule Set.
Conclusion: order beats individual measures
A secure Apache configuration for PHP consists of a few well-understood building blocks. PHP runs separately from the web server via PHP-FPM, .htaccess is not evaluated, version information and TRACE are switched off, size limits match PHP, and writable folders do not execute scripts. None of these points is spectacular on its own. Together they ensure that a mistake in one place does not immediately lead to a takeover of the server.
For management and project leads, a second property is almost more important: a tidy configuration is traceable. If all rules are in the VirtualHost instead of scattered across dozens of .htaccess files, you can quickly check at the next critical vulnerability which website is connected how. In a situation like October 2026, that saves exactly the time that matters.
Still plan the changeover calmly and with a test environment. A rushed switch from mod_php to PHP-FPM shortly before a weekend creates new problems rather than solving them.
How does PHP SOS help?
- Check the current state: We read out the entire Apache configuration, all active modules and all
.htaccessfiles. - Plan the switch: Where mod_php is still running, we prepare the move to PHP-FPM with separate pools per website.
- Harden the configuration: We set core options, transfer
.htaccessrules into the VirtualHosts and block writable folders. - Test and document: After every change we check the websites from outside and record the state as part of server hardening.
Frequently asked questions
Should I still use mod_php?
For new setups we recommend PHP-FPM via mod_proxy_fcgi. The PHP team rates the risk from PHP2Shell highest for mod_php. With PHP-FPM, PHP runs separately from the web server, with its own users per website and its own limits.
Why AllowOverride None if my CMS needs .htaccess?
An .htaccess can be created by anyone who may write in the web root, including injected code. With AllowOverride None, Apache does not evaluate such files. You then transfer your CMS's rules once into the VirtualHost. That takes some effort but makes the configuration traceable and faster.
Can I set ServerTokens differently per website?
No. According to the Apache documentation, ServerTokens applies to the entire server and cannot be changed per VirtualHost. It also controls the version information below error pages that ServerSignature displays.
What is the default value of LimitRequestBody?
Since Apache 2.4.54 the default is 1 GiB; in version 2.4.53 and older it was unlimited. Set the value to match post_max_size in your PHP configuration so that oversized requests are already rejected in the web server.