One Apache config for multiple environments

Today I added on to a bit of Apache configuration I wrote several years ago and was impressed by how past me had done it, so thought I'd write a blog post.

Challenge:

  • Have one Apache VirtualHost config that can be deployed to all of an app's environments, supporting settings that vary by environment
  • In particular, need to have environment-specific proxy to different backend servers
  • Do it simply and quickly, without having to do messy things like parse the request hostname

Caveats:

  • Separate environments are running separate Apache servers
  • Per-environment ServerName/ServerAliases are not needed – in my case nothing in the proxied apps are using host names, so this is fine, though I could have injected the header before proxying if needed
  • Strive for simplicity, because if abused this concept could quickly become a tangled nightmare

Solution:

Create a file on each Apache server that is loaded into the config (in our case a simple file in conf.d that is auto-included) and sets an environment variable, like this for a prod server.

SetEnvIf Server_Addr ^ APACHE_ENV=prod

I'm using SetEnvIf here because it runs early in the request pipeline and can be used in Rewrite statements – basic SetEnv runs later in the pipeline, after SetEnvIf and Rewrite.

I'm using Server_Addr in the match on the basis that it's an internal variable that's always set and valid, and safe to match on -- which is not always guaranteed for some of the other SetEnvIf matches that come from request headers.

Once the environment variable is set you can use it to set other environment variables to do stuff, for example to proxy to different backend hosts by environment.

SetEnvIf APACHE_ENV prod backend_api=https://prod-api-host
SetEnvIf APACHE_ENV dev backend_api=https://dev-api-host
[...]

RewriteRule (/api/.*) %{env:api_backend}$1 [P,L]

You can also do interesting things like simply password protect only certain environments -- in this case password protect all environments except prod.

SetEnvIf APACHE_ENV prod noauth
# [SetEnvIF based on other criteria here if wanted]

AuthName ...
AuthType ...
Require valid-user
Requre env noauth

It's a remarkably simple and elegant solution, and I'm prod of past me for implementing it in a way that's been working well for years.