Using IfDefine with Environment Variables in Apache

apache-2.4Apache2environment-variables

I am trying to make my configuration as flexible and easy-to-configure as possible by using a list of variables in /etc/sysconfig and loading the environment variables when Apache is started using a systemd drop-in configuration file. Since the environment variables don't change after the server is started, I want to improve performance and flexibility by using Apache's IfDefine directive instead of the regular If directive.

One of the problems with the regular <If>... </If> directive is that the directives inside the block are always evaluated even if they are not executed. This is very important if you are using environment variables as parameters, like this:

SVNParentPath ${MY_SVN_REPOSITORIES_PATH}

In this case, Apache will throw an error and refuse to start if ${MY_SVN_REPOSITORIES_PATH} evaluates to an empty string. I want to disable serving repositories when the system environment variable MY_SVN_REPOSITORIES_PATH is empty or not set. How do I do this?

Best Answer

This is an ugly hack, but it will work. What we do here is we use the Define directive to define a variable whose name includes the value of the environment variable, then check if that variable is defined:

Define "Test${MY_SVN_REPOSITORIES_PATH}"
<IfDefine Test>
    # the variable is set to an empty string
</IfDefine>
<IfDefine !Test>
    # SVN repositories are configured or the variable is unset
</IfDefine>
<IfDefine "Test/srv/svn">
    # SVN repositories are configured to be in /srv/svn
</IfDefine>

If ${MY_SVN_REPOSITORIES_PATH} evaluates to an empty string, the configuration parameter "Test" will be defined. If the environment variable evaluates to something else (possibly the literal ${MY_SVN_REPOSITORIES_PATH} if the environment variable is unset) then the configuration parameter "Test" (by itself) will not be defined.

There is one limitation here: we can test for specific values, including an empty string, but we cannot test for whether the variable is unset using only the IfDefine directive. This is because there is no way to put the literal dollar-sign and brackets characters into a an IfDefine directive without having Apache try to expand them1. Without an escape character, you'll never know whether Apache is comparing with the literal variable name or its value.

But, there's an ugly hack to work around that, too!

# Prevent environment variable expansion by splitting the variable name into two literals
Define FirstPartOfVariableName "${MY_SVN_"
Define SecondPartOfVariableName "REPOSITORIES_PATH}"

Define "Test${MY_SVN_REPOSITORIES_PATH}"
<IfDefine "Test">
    # Environment variable is set to an empty string
</IfDefine>
<IfDefine Test${FirstPartOfVariableName}${SecondPartOfVariableName}>
    # Environment variable is not set
</IfDefine>
<IfDefine !Test>
    <IfDefine !Test${FirstPartOfVariableName}${SecondPartOfVariableName}>
        # The environment variable is set, and is not an empty string.
    </IfDefine>
</IfDefine>

Here, we've circumvented the environment variable expansion so that we can identify the three main cases we care about during configuration. You can't combine IfDefine directives with an OR operation, so "not set" and "empty string" are stuck being separate blocks, but you can either copy and paste your configuration between the blocks or check out mod_macro.


  1. I checked Apache's source code. The function is ap_resolve_env in core.c, and the functions that call it seem to only disable the call when a compile-time switch is set.