This is a tutorial on how to setup dynamically configured mass virtual hosting in Apache as well as multiple versions of PHP so you can easily run different sites on different PHP versions with no need for additional configuration or manually switching the PHP version to access a site on a given PHP version. There are some other articles on the internet about this as well, but as always everyone's experience differs slightly and there is no one definitive article that works for everyone. I am not aiming to achieve that either, just offering what worked for me.

This is also not a beginner's tutorial. It expects that you already have a very good knowledge of how to install and configure Apache using Homebrew on macOS. If you want a tutorial on how to get the basics setup on your computer, just google it and you'll find lots of results.

Pre-requisites:

  • Apache 2.4 installed
  • Multiple versions of PHP installed with Homebrew
  • Some sort of DNS configuration (eg dnsmasq) that automatically points all your desired domain patterns at your localhost
  • Sites already setup and working in, for example, /Users/yourusername/Sites

Configure and Start PHP-FPM

The first step is to open the php-fpm www.conf files for each version of PHP and specify a port number for each version to listen on. This is the key to running multiple versions without conflict.

With PHP installed with Homebrew on macOS, you'll find the conf files you you need to edit here:

/usr/local/etc/php/[version-number]/php-fpm.d/www.conf

In this conf file for each PHP version, look for the following line of code and change the port number from 9000 to 90[php-version-number], eg 9074, 9080 etc.

listen = 127.0.0.1:9000

Next, start the php-fpm service for each version of PHP, for example:

$ brew services start php@7.4
$ brew services start php@8.0

Apache Configuration

The next step is to configure apache to use the appropriate PHP service based on the domain name pattern.

First, you need to disable mod_php and enable other modules needed to run PHP as fast cgi. Open up your main httpd.conf and find the section where all the modules are loaded. First, comment out or remove all lines that load PHP modules.

Next, you will need to comment out mod_mpm_prefork and enable mod_mpm_event instead:

LoadModule mpm_event_module lib/httpd/modules/mod_mpm_event.so
#LoadModule mpm_prefork_module lib/httpd/modules/mod_mpm_prefork.so

You will also need to make sure the FCGI module is enabled:

LoadModule proxy_fcgi_module lib/httpd/modules/mod_proxy_fcgi.so

Also make sure that mod_proxy itself is enabled, as that is a dependency:

LoadModule proxy_module lib/httpd/modules/mod_proxy.so

Next, go to whatever conf file contains configuration specific to PHP, particularly where it has the configuration that sets the handler to use for PHP files. You may have something like this:

<FilesMatch \.php$>
  SetHandler application/x-httpd-php
</FilesMatch>

Replace it with this:

<FilesMatch \.(php|phar)$>
  SetHandler "proxy:fcgi://127.0.0.1:9080"
</FilesMatch>

This will serve as the default PHP version to use. Change the port number to whichever one you want to be the default fallback if not otherwise defined by a virtual host configuration.

Virtual Host Configuration

When using dynamic mass virtual hosts, you should have a virtual host configuration that looks something like this:

<VirtualHost *>
    UseCanonicalName Off
    VirtualDocumentRoot /Users/someuser/Sites/%1
</VirtualHost>

You may also have some additional directives in your virtual host configuration, such as CustomLog or CacheEnable, which are not discussed here.

With the above configuration, you could visit http://some-site-name.test, if that's the fake TLD you have routed to your localhost for example, and it would load the site contained in a folder at /Users/someuser/Sites/some-site-name.

In order to be able to load any given site on a specific version of PHP, you just need to define a ServerAlias directive that includes something to differentiate the PHP version as well as a directive to set the specific PHP handler to the desired version. Here is an example of the updated vhost configuration for PHP 7.4:

<VirtualHost *>
    UseCanonicalName Off
    ServerAlias *.p74.test
    VirtualDocumentRoot /Users/someuser/Sites/%1

    <FilesMatch \.(php|phar)$>
        SetHandler "proxy:fcgi://127.0.0.1:9074"
    </FilesMatch>
</VirtualHost>

Once you have completed all the Apache configuration, restart the httpd service:

$ brew services restart httpd

Now you can visit http://some-site-name.p74.test and it will load the same site from the same folder running PHP 7.4. You can add additional virtual host configurations for each PHP version you want to use, just modifying the ServerAlias and SetHandler accordingly. Restart the httpd service after you have completed all your virtual host configuration.

Once you have this setup, you can run any given local site on any given version of PHP that you have installed simply by visiting it with a different URL that contains your PHP version differentiator.

Note that if you change the PHP configuration for any given version of PHP, you will then need to restart that particular PHP version's service, for example:

$ brew services restart php@7.4

It is not necessary to restart the httpd for PHP configuration changes to take effect.