PHP and Apache have been running the web together for decades, and it’s still one of the most common stacks I set up for clients, whether it’s a fresh WordPress install or a custom application. In this guide, I’ll walk through installing PHP, connecting it to Apache the modern way, and configuring it properly for production use.
Understanding How PHP Works with Apache
There are two main ways to run PHP alongside Apache:
- mod_php – PHP runs as an Apache module, embedded directly in Apache’s worker processes. It’s simple to set up but ties PHP’s performance and stability to Apache’s process model, and only works with the older
mpm_preforkmodule. - PHP-FPM (FastCGI Process Manager) – PHP runs as an independent process pool, and Apache communicates with it via FastCGI (
mod_proxy_fcgi). This is the modern, recommended approach: it’s faster, more memory-efficient, and works with Apache’s high-performancempm_eventmodule.
This guide focuses primarily on PHP-FPM, since it’s the standard for new deployments, with a note on mod_php for completeness.
Prerequisites
- A Linux server with Apache 2.4+ installed
- Root or sudo access
- Basic familiarity with the command line
Installing PHP-FPM and Connecting It to Apache
Step 1: Install PHP-FPM
Debian/Ubuntu:
sudo apt update
sudo apt install php-fpm php-mysql php-curl php-gd php-mbstring php-xml php-zip
CentOS/RHEL:
sudo dnf install epel-release
sudo dnf install php-fpm php-mysqlnd php-curl php-gd php-mbstring php-xml php-zip
Check the installed version:
php -v
Step 2: Switch Apache to mpm_event and Enable FastCGI Proxy Modules
sudo a2dismod mpm_prefork
sudo a2enmod mpm_event
sudo a2enmod proxy_fcgi
sudo a2enmod setenvif
sudo a2enconf php8.3-fpm
sudo systemctl restart apache2
(Replace 8.3 with your installed PHP version — check with php -v.)
Step 3: Start and Enable PHP-FPM
sudo systemctl start php8.3-fpm
sudo systemctl enable php8.3-fpm
sudo systemctl status php8.3-fpm
Step 4: Configure the Virtual Host
ServerName example.com
DocumentRoot /var/www/example.com/public_html
AllowOverride All
Require all granted
SetHandler "proxy:unix:/run/php/php8.3-fpm.sock|fcgi://localhost/"
ErrorLog ${APACHE_LOG_DIR}/example.com_error.log
CustomLog ${APACHE_LOG_DIR}/example.com_access.log combined
Enable the site and restart Apache:
sudo a2ensite example.com.conf
sudo apachectl configtest
sudo systemctl restart apache2
Step 5: Test PHP Is Working
sudo nano /var/www/example.com/public_html/info.php
Visit http://example.com/info.php in a browser. You should see the PHP configuration page. Delete this file after testing — it exposes detailed server information that shouldn’t be public.
sudo rm /var/www/example.com/public_html/info.php
Alternative: Installing mod_php (Legacy Approach)
If you have a specific reason to use mod_php (some legacy applications assume it), here’s the setup:
sudo apt install libapache2-mod-php
sudo a2dismod mpm_event
sudo a2enmod mpm_prefork
sudo a2enmod php8.3
sudo systemctl restart apache2
Note that mod_php requires mpm_prefork, which handles one request per process/thread and generally performs worse under high concurrency than mpm_event with PHP-FPM.
Configuring php.ini for Production
Locate your php.ini file:
php --ini
For FPM, it’s typically at /etc/php/8.3/fpm/php.ini. Key production settings to review:
; Hide PHP version from response headers (security)
expose_php = Off
; Disable dangerous functions if not needed
disable_functions = exec,passthru,shell_exec,system,proc_open,popen
; Limit file upload size appropriately
upload_max_filesize = 20M
post_max_size = 20M
; Set a reasonable execution time limit
max_execution_time = 30
; Enable OPcache for performance
opcache.enable = 1
opcache.memory_consumption = 128
opcache.max_accelerated_files = 10000
opcache.revalidate_freq = 60
; Error handling - never display errors in production
display_errors = Off
log_errors = On
error_log = /var/log/php/error.log
After editing, restart PHP-FPM:
sudo systemctl restart php8.3-fpm
Tuning PHP-FPM Process Management
Edit the pool configuration, typically at /etc/php/8.3/fpm/pool.d/www.conf:
pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 10
pm.max_requests = 500
pm.max_childrenshould be calculated based on available RAM divided by average memory per PHP process.pm.max_requestsrecycles worker processes periodically, helping mitigate memory leaks in long-running scripts.
Restart after changes:
sudo systemctl restart php8.3-fpm
Real-World Use Cases
- WordPress, Drupal, Joomla – the vast majority of CMS platforms run on PHP and Apache.
- E-commerce platforms – Magento and WooCommerce (WordPress-based) rely on this stack heavily.
- Custom web applications – frameworks like Laravel and Symfony run on PHP-FPM behind Apache in most production deployments.
- Shared hosting environments – many hosting providers still default to Apache + PHP-FPM for its balance of compatibility and performance.
Common Mistakes to Avoid
- Leaving phpinfo() accessible in production – This exposes internal paths, loaded modules, and configuration details useful to attackers.
- Using mod_php with mpm_event – This combination doesn’t work; mod_php requires
mpm_prefork. - Not matching the socket path in Apache config to the actual PHP-FPM socket – A common cause of 502 errors after a PHP version upgrade.
- Ignoring pm.max_children tuning – Leaving it at low defaults on a high-traffic site causes requests to queue and time out.
- Displaying errors in production –
display_errors = Onleaks file paths and code structure to visitors; always log errors instead.
Security Best Practices
- Set
expose_php = Offto avoid advertising your PHP version in HTTP headers. - Disable unused, dangerous PHP functions (
exec,shell_exec,system) unless your application specifically needs them. - Keep PHP updated — end-of-life PHP versions no longer receive security patches and are a common attack vector.
- Run PHP-FPM pools under a dedicated, non-privileged user, separate from Apache’s user where possible, for better isolation between sites on shared servers.
- Set strict
upload_max_filesizeand validate uploaded file types at the application level, not just via PHP config. - Use
open_basedirrestrictions to confine PHP scripts to specific directories, limiting the blast radius if a script is compromised.
Performance Optimization Tips
- Always enable OPcache — it caches compiled PHP bytecode, dramatically reducing CPU usage on repeated requests.
- Use PHP-FPM with mpm_event rather than mod_php with mpm_prefork for better concurrency handling.
- Combine with the compression and caching techniques from our other Apache guides for full-stack performance gains.
- Monitor PHP-FPM’s status page (
pm.status_path) to track pool utilization and tunepm.max_childrenbased on real traffic data. - Consider a PHP-level object cache (like Redis or Memcached) for applications with expensive, repeated database queries.
Troubleshooting
502 Bad Gateway:
- PHP-FPM isn’t running, or the socket path in Apache’s config doesn’t match the actual socket. Check with
sudo systemctl status php8.3-fpmand verify the path in/etc/php/8.3/fpm/pool.d/www.conf(listen = /run/php/php8.3-fpm.sock).
White screen with no error message:
display_errorsis off (correct for production) but nothing is being logged. Checkerror_login php.ini and Apache’s error log.
“File not found” errors for .php files:
- Verify
DirectoryIndexincludesindex.phpin your Apache config, and that theFilesMatchhandler block is correctly scoped to the virtual host.
High memory usage:
- Review
pm.max_children— too high a value on limited RAM causes the OOM killer to terminate processes unpredictably.
Frequently Asked Questions
Should I use PHP-FPM or mod_php for a new project? PHP-FPM, in almost all cases. It’s faster, more memory-efficient, and compatible with Apache’s modern mpm_event MPM.
How do I run multiple PHP versions on the same server? Install multiple PHP-FPM versions side by side (e.g., php8.1-fpm and php8.3-fpm), each with its own socket, and point different virtual hosts to the appropriate socket in their FilesMatch block.
Do I need to restart Apache after changing php.ini? No — restart PHP-FPM instead (sudo systemctl restart php8.3-fpm), since PHP-FPM manages its own processes independently of Apache.
Is PHP still relevant in 2026? Yes. PHP continues to power a substantial share of the web, particularly through WordPress and other CMS platforms, and modern PHP (8.x) with OPcache and PHP-FPM is a solid, performant choice.
Summary and Key Takeaways
- PHP-FPM with
mod_proxy_fcgiand Apache’smpm_eventis the modern, recommended way to run PHP on Apache. - mod_php still works but requires the older, less concurrent
mpm_preforkMPM. - Always disable
display_errorsandexpose_phpin production, and remove testphpinfo()files immediately after use. - Enable OPcache and tune
pm.max_childrenbased on your server’s available memory for the best performance. - Check PHP-FPM’s status and the Apache error log first when diagnosing 502 errors or blank pages.