How to Install and Configure PHP on Apache

How to install and configure PHP on Apache

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:

  1. 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_prefork module.
  2. 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-performance mpm_event module.

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_children should be calculated based on available RAM divided by average memory per PHP process.
  • pm.max_requests recycles 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

  1. Leaving phpinfo() accessible in production – This exposes internal paths, loaded modules, and configuration details useful to attackers.
  2. Using mod_php with mpm_event – This combination doesn’t work; mod_php requires mpm_prefork.
  3. 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.
  4. Ignoring pm.max_children tuning – Leaving it at low defaults on a high-traffic site causes requests to queue and time out.
  5. Displaying errors in productiondisplay_errors = On leaks file paths and code structure to visitors; always log errors instead.

Security Best Practices

  • Set expose_php = Off to 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_filesize and validate uploaded file types at the application level, not just via PHP config.
  • Use open_basedir restrictions 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 tune pm.max_children based 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-fpm and 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_errors is off (correct for production) but nothing is being logged. Check error_log in php.ini and Apache’s error log.

“File not found” errors for .php files:

  • Verify DirectoryIndex includes index.php in your Apache config, and that the FilesMatch handler 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_fcgi and Apache’s mpm_event is the modern, recommended way to run PHP on Apache.
  • mod_php still works but requires the older, less concurrent mpm_prefork MPM.
  • Always disable display_errors and expose_php in production, and remove test phpinfo() files immediately after use.
  • Enable OPcache and tune pm.max_children based 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.

References

Total
3
Shares

Leave a Reply

Previous Post
How to set up Apache for serving dynamic content

How to Set Up Apache for Serving Dynamic Content

Next Post
How to install and configure Python with Apache

How to install and configure Python with Apache

Related Posts