01 Dec 2018

Bulk Updating Apache VirtualHost Ports

Is there a way to use variables in an Apache config file? Specifically for the port number in a `VirtualHost` block? The answer is, kind of...

Why is this even a problem?

My VPS is a mix between a productive workshop and a dumping ground for dubious scripts and half-completed projects. It’s messy but mostly effective. A key part of this is the 30+ Apache config files that I've forgotten about run important projects and websites.

I recently had this idea, which I’ll probably write about more in the future, where I wanted to proxy different sub-domains to different private IPs. I couldn’t think of a way to do this dynamically in Apache, although I’m confident it probably can be done by an Apache wizard, so I decided to use a method I knew a bit more about involving HAProxy and a local DNS server. To do this though I needed to free up port 80. Easy enough, just move all my Apache config files that use port 80 to something else, then put HAProxy up to either handle these weird sub-domain/private IP requests otherwise just pass them through to Apache as before.

Seconds before starting to edit the first file it occurred to me that I probably wouldn’t keep the setup long term. It was time to see if I could make the change utilising some config variable for a port number instead of editing these files multiple times.

Ideas and Solutions

Idea 1 was to do some scripted combination of find and sed to rewrite the VirtualHost *:80 lines. It feels messy though, and it’s exactly the sort of script I’ll forget about when it comes to reverting the change.

Idea 2 was to use environment variables. I’ve used environment variables in Apache files before, but it turns out this doesn’t seem to work for the port in a VirtualHost block. I tried as well with direct Define directives before the relevant block, but this also didn’t seem to work.

Idea 3 came from this StackOverflow post, which was to use macros. Unbeknown to me the mod_macro module has been included with Apache since version 2.4. Enabling (on Ubuntu at least) is as easy as a2enmod macro and systemctl restart apache2. With the macro module enabled we can then create a file similar to this:

# cat /etc/apache2/conf-enabled/vhost.macro.conf 
<Macro DefaultVHost>
  # Let's keep port 80 free by default
  <VirtualHost *:81>

Then for each of our Apache config files we replace the usual <VirtualHost *:80> line with Use DefaultVHost. In the future we just update the macro and reload the config to change all the VirtualHost ports back again.

One slight downside to this is that it makes the syntax look strange as you’ll still have a closing </VirtualHost> line in your config, and that just doesn’t feel right. Perhaps having two macros, DefaultVHostOpen and DefaultVHostClose is a neat way to get around that and still keep the port easy to update.

Update: It turns out the other downside to this is that certbot can’t auto-parse the apache config files anymore, so if you rely on that as part of generating certificates it’s probably a deal breaker!

Dev SysAdmin
Back to posts