PHP and Apache have a long history together, and mod_php is the classic way to get them talking to each other. I’ve used it on plenty of smaller sites and legacy applications over the years — it’s dead simple to set up compared to alternatives like PHP-FPM, even if it’s not always my first choice for high-traffic production environments anymore. In this post, I’ll walk through installing and configuring mod_php, and I’ll also be honest with you about when I’d reach for something else instead.
What Is mod_php
mod_php is an Apache module that embeds the PHP interpreter directly inside Apache’s worker processes. When a request comes in for a .php file, Apache hands it off internally to the embedded PHP interpreter, which executes the script and returns the output — all within the same process, no separate service required.
This is different from PHP-FPM (FastCGI Process Manager), which runs PHP as a completely separate process pool that Apache (or Nginx) communicates with over a socket or TCP connection. mod_php is simpler to set up, but it ties PHP execution to Apache’s process model, which has real implications for performance and memory usage that I’ll get into.
Prerequisites
- Apache installed and running (with the
preforkMPM — this matters, more below) - Root or sudo access
- Basic familiarity with PHP and Apache virtual hosts
Important: mod_php Requires the Prefork MPM
This is the detail that trips people up most often. mod_php is not thread-safe, so it only works with Apache’s prefork Multi-Processing Module, which handles each request in a separate process rather than a thread. If your Apache is running worker or event MPM (common on fresh installs), you’ll need to switch.
Check your current MPM:
apachectl -V | grep -i mpm
Switch to prefork on Debian/Ubuntu:
sudo a2dismod mpm_event mpm_worker
sudo a2enmod mpm_prefork
sudo systemctl restart apache2
On CentOS/RHEL, edit /etc/httpd/conf.modules.d/00-mpm.conf and comment/uncomment the relevant LoadModule lines to select mpm_prefork_module.
Step 1: Install PHP and mod_php
Debian/Ubuntu
sudo apt update
sudo apt install php libapache2-mod-php
This automatically enables the module and restarts Apache appropriately, but I always double check:
apachectl -M | grep php
CentOS/RHEL
sudo dnf install php php-cli
sudo systemctl restart httpd
On RHEL-based systems, the Apache PHP module typically loads automatically once the package is installed — confirm with:
apachectl -M | grep php
Step 2: Verify PHP Is Working
Create a test file in your document root:
sudo nano /var/www/html/info.php
<?php
phpinfo();
?>
Visit http://your-server-ip/info.php in a browser. If you see the PHP info page, everything’s wired up correctly.
Important: Delete this file once you’ve confirmed it works. phpinfo() exposes a lot of server configuration detail that shouldn’t be publicly accessible.
sudo rm /var/www/html/info.php
Step 3: Configure PHP Settings
The main PHP config file is php.ini, typically located at:
- Debian/Ubuntu:
/etc/php/8.x/apache2/php.ini - CentOS/RHEL:
/etc/php.ini
Common settings I adjust right away:
memory_limit = 256M
upload_max_filesize = 64M
post_max_size = 64M
max_execution_time = 60
display_errors = Off
log_errors = On
error_log = /var/log/php/error.log
In production, display_errors should always be Off — you don’t want PHP stack traces visible to visitors. Errors should go to a log file instead, where you can review them without exposing internal details.
After editing php.ini, restart Apache to apply changes:
sudo systemctl restart apache2 # Debian/Ubuntu
sudo systemctl restart httpd # CentOS/RHEL
Step 4: Per-Directory PHP Configuration
Sometimes I need different PHP settings for a specific site or directory — for instance, a larger upload limit just for an admin upload tool. I do this in the virtual host or a .htaccess file:
<Directory /var/www/example.com/uploads>
php_value upload_max_filesize 128M
php_value post_max_size 128M
</Directory>
Note: php_value/php_admin_value directives only work with mod_php — this is actually one of its conveniences over PHP-FPM, where per-directory PHP settings require separate pool configs.
Installing PHP Extensions
Most applications (WordPress, Laravel, etc.) need specific PHP extensions. Common ones:
# Debian/Ubuntu
sudo apt install php-mysql php-curl php-gd php-mbstring php-xml php-zip
# CentOS/RHEL
sudo dnf install php-mysqlnd php-curl php-gd php-mbstring php-xml php-zip
Restart Apache after installing any new extension:
sudo systemctl restart apache2 # Debian/Ubuntu
sudo systemctl restart httpd # CentOS/RHEL
mod_php vs PHP-FPM: My Honest Take
I want to be upfront about this since it affects real decisions:
mod_php pros:
- Simpler setup, fewer moving parts
- Per-directory config via
.htaccessworks natively - Good enough for small sites, internal tools, and low-traffic projects
mod_php cons:
- Requires the
preforkMPM, which is heavier on memory (each Apache process embeds a full PHP interpreter) - Can’t take advantage of
event/workerMPM’s better concurrency handling - Doesn’t scale as well under high concurrent load compared to PHP-FPM
For anything with meaningful traffic, I switch to PHP-FPM with Apache’s event MPM and mod_proxy_fcgi. But for smaller sites, dev environments, or legacy applications that assume mod_php-style behavior, it’s still a perfectly reasonable choice, and I won’t pretend otherwise.
Switching PHP Versions with mod_php
Because mod_php binds a single PHP version directly into Apache, switching versions on Debian/Ubuntu (where multiple PHP versions can be installed side by side) requires disabling one module and enabling another:
sudo a2dismod php8.1
sudo a2enmod php8.3
sudo systemctl restart apache2
Check which version is now actually active:
php -v
apachectl -M | grep php
This works fine for a single site that needs to move to a newer PHP version, but if you’re hosting multiple sites that each need a different PHP version simultaneously, mod_php genuinely can’t do that — only one version can be loaded into Apache at a time. That’s the scenario where I stop reaching for mod_php entirely and set up PHP-FPM instead, running a separate pool per version, each proxied through mod_proxy_fcgi to the appropriate socket per virtual host.
Real-World Use Cases
- Small business websites with light traffic where simplicity matters more than raw performance.
- Legacy PHP applications that were built assuming
mod_php‘s behavior (e.g., certain.htaccess-based PHP config overrides). - Local development environments where ease of setup outweighs performance concerns.
- Shared hosting-style setups where per-directory PHP configuration via
.htaccessis genuinely useful.
Troubleshooting Tips
- PHP files downloading instead of executing: Usually means
mod_phpisn’t loaded — checkapachectl -M | grep php. - 500 Internal Server Error after enabling PHP: Check the Apache error log first; it’s almost always a specific PHP error or a permissions issue.
- “AH00534: Configuration error: mod_php… requires prefork MPM”: You’re running
eventorworkerMPM — switch topreforkas shown above. - Changes to php.ini not taking effect: Confirm you edited the Apache-specific
php.ini(not the CLI one), and restart (not just reload) Apache.
Common Mistakes to Avoid
- Leaving
phpinfo()accessible publicly — remove test files immediately after checking them. - Running
event/workerMPM with mod_php enabled, which simply won’t work. - Leaving
display_errors Onin production, exposing internal paths and logic to visitors. - Forgetting to restart Apache after installing new PHP extensions.
- Assuming mod_php will scale like PHP-FPM under heavy load — it won’t, due to the process-per-request model tied to Apache’s prefork workers.
Security Best Practices
- Always set
display_errors = Offandlog_errors = Onin production. - Disable dangerous PHP functions you don’t need, via
disable_functionsinphp.ini(e.g.,exec,shell_exec,passthruif unused). - Keep PHP updated — old PHP versions accumulate known vulnerabilities quickly.
- Set
expose_php = Offinphp.inito avoid leaking your PHP version in response headers. - Restrict file upload sizes and types appropriately for your application.
Performance Optimization
- Enable OPcache — it’s bundled with PHP and dramatically speeds up execution by caching compiled bytecode:
opcache.enable=1
opcache.memory_consumption=128
opcache.max_accelerated_files=10000
opcache.revalidate_freq=2
- Tune
preforkMPM settings (MaxRequestWorkers,StartServers) to match your server’s available memory, since every worker embeds a full PHP interpreter. - For anything beyond low-to-moderate traffic, seriously consider migrating to PHP-FPM for better resource efficiency.
Frequently Asked Questions
Q: Do I need mod_php or can I just install PHP separately? A: For Apache to execute PHP files directly, you need either mod_php or a FastCGI setup like PHP-FPM with mod_proxy_fcgi. PHP alone isn’t enough.
Q: Why does mod_php require the prefork MPM? A: Because mod_php isn’t thread-safe — it needs each request handled in its own process, which is exactly what prefork provides.
Q: Is mod_php deprecated? A: Not deprecated, but increasingly less recommended for production due to performance and scalability limitations compared to PHP-FPM.
Q: Can I run multiple PHP versions with mod_php? A: It’s difficult — mod_php binds one PHP version directly into Apache. Running multiple versions simultaneously is much easier with PHP-FPM, where you can run separate pools per version.
Q: How do I check which PHP version is running? A: Run php -v from the CLI, or check phpinfo() output temporarily (then remove it).
Summary and Key Takeaways
mod_php is a straightforward way to get PHP running under Apache, especially for smaller projects. Here’s what to remember:
- It requires the
preforkMPM — check and switch if needed. - Install via your package manager, verify with a temporary
phpinfo()file, then remove it. - Configure
php.inisensibly for production (errors off, logging on, reasonable limits). - For higher-traffic sites, PHP-FPM is generally the better long-term choice.
- Enable OPcache regardless of which approach you use — it’s an easy, significant performance win.
It’s a solid, simple option — just go in with clear eyes about its scaling limitations.