chungn

Streamlining Multiple PHP Projects with Apache Virtual Hosts

As a PHP developer, I often find myself working on multiple projects, each requiring a different PHP version. In the past, setting up each project involved creating a new Apache (or Nginx) configuration file and adding an entry to my /etc/hosts file. While this process isn’t overly complex, I’ve always been on the lookout for ways to optimize it. After some exploration and experimentation, I’ve found a solution that eliminates the need for separate configurations for each project.

Dynamic Virtual Hosts with Apache

I achieved this by using a dynamic Apache virtual host configuration, which simplifies project setup. The configuration are as follows:

<VirtualHost *:80>
    ServerAlias *.php81
    VirtualDocumentRoot /var/www/html/%2/%1

    <IfModule dir_module>
        DirectoryIndex index.html
    </IfModule>

    ###
    # %1 is first group captured by RewriteCond (matched against %{HTTP_HOST} eg: "public.example.php81")
    # $1 is first group captured by RewriteRule (matched against eg: "/app1/index.html")
    # Reference: https://httpd.apache.org/docs/2.4/mod/mod_rewrite.html
    ###
    RewriteEngine On
    RewriteCond %{HTTP_HOST} ^([^.]+).([^.]+).php81$ [NC]
    RewriteRule ^\/(.*\.php(\/.*)?)$ fcgi://php81:9000/var/www/html/%2/%1/$1 [P]
</VirtualHost>

As you can easily see from this config, there are 3 differences from a normal/simple virtual host config:

  • ServerAlias: this directive is straightforward to understand, instead of ServerName and a fixed name, this allows wildcard in value.
  • VirtualDocumentRoot: this is a bit more intricate, what is %2 and %1 in that path?
    • %1 is the part before the first dot in server name. A request for http://example.chungn.com, %1 will be example, similar for %2 and %N
    • we can even have syntax like %N.M where M selects characters in N part, you can learn more in apache docs
    • this directive requires vhost_alias_module
  • RewriteCond and RewriteRule: this one is the most tricky one. I’ve provided comments to clarify their roles.
    • RewriteCond is a regex match, in this setup, the former part is the target, the latter is regex. Let me explain regex in this example: ^([^.]+).([^.]+).php81$
      • first, this will match domain with this pattern: *.*.php81
      • second, this regex captures first 2 parts separated by . in this host, they are used in RewriteRule
    • RewriteRule similarly accepts 2 arguments, the first one is request path, and the 2nd one is target path. Interesting part is target path can use regex matches captured by RewriteCond. For this case, with our example host http://example.chungn.com/, webserver will request to files in /var/www/html/chungn/example/
    • With above config, apache will proxy the request from *.*.php81 to php81:9000. I can go further with a bunch of RewriteRule and RewriteCond for *.*.php(d+) and php%3:9000 and this will work with multiple PHP version. This make testing a PHP module/website with multiple PHP version much easier for me.

Note: While this configuration is fantastic for development purposes, it may not be suitable for a production environment due to potential performance issues.

To complete this setup, you’ll need to use a tool like dnsmasq, which I may cover in a future post.

By implementing this dynamic virtual host configuration, you can streamline your workflow and eliminate the need for separate Apache configurations for each project, making PHP development more efficient and enjoyable.