I want to deny when two conditionals are set.
I have set the variable in my apache2.conf
:
SetEnv MY_SITE stage
and then in .htaccess
I am checking that the header and env variable match up.
# check the variable is set by outputting to header.
RewriteCond %{ENV:MY_SITE} (.+)
RewriteRule ^ - [E=MY_SITE:%1]
Header always set X-Site %{MY_SITE}e
And then deny access if the header is wrong and this is staging:
# check the azure header and that this is staging
RewriteCond %{HTTP:X-Azure-FDID} !^xxxxxx$ [NC]
RewriteCond %{ENV:MY_SITE} =stage
RewriteRule ^ - [F]
However the second conditional does not result in the forbidden message.
If I try this, it works and denys access:
RewriteCond %{HTTP:X-Azure-FDID} !^xxxxxx$ [NC]
# RewriteCond %{ENV:MY_SITE} =stage
RewriteRule ^ - [F]
But this does not:
RewriteCond %{ENV:MY_SITE} =stage
RewriteRule ^ - [F]
Why does the second conditional not work?
SetEnv MY_SITE stage
SetEnv
(mod_env) is processed too late for mod_rewrite to pick it up (mod_rewrite is processed early). You need to use SetEnvIf
(mod_setenvif) to set the env var instead, which is processed much earlier.
As noted in the docs for SetEnv
:
The internal environment variables set by this directive are set after most early request processing directives are run, such as access control and URI-to-filename mapping. If the environment variable you're setting is meant as input into this early phase of processing such as the RewriteRule directive, you should instead set the environment variable with SetEnvIf.
SetEnvIf
has a different syntax. To set this unconditionally, you would need to do something like the following instead:
SetEnvIf ^ ^ MY_SITE=stage
The first argument (^
) is a regex that matches any request header. ^
is always successful.
The second argument (another ^
) is a regex that matches against the preceding argument value. Again, this is always successful.
When using env vars and mod_rewrite in a .htaccess
context you need to be careful of any looping by the rewrite engine (eg. a standard front-controller pattern later in the .htaccess
file could trigger this). If there is then on the second pass by the rewrite engine any env vars that are set during the first pass are renamed with a REDIRECT_
prefix (eg. REDIRECT_MY_SITE
). Third pass, REDIRECT_REDIRECT_MY_SITE
etc. etc. So you may need to check for a REDIRECT_<var>
instead or prevent additional passes by the rewrite engine. (mod_rewrite directives in a server or virtualhost context do not have this additional complexity by default.)
# check the variable is set by outputting to header. RewriteCond %{ENV:MY_SITE} (.+) RewriteRule ^ - [E=MY_SITE:%1] Header always set X-Site %{MY_SITE}e
In light of the above, this does not test what you think it does. In this case, the mod_rewrite rule does nothing (since MY_SITE
is not set when this rule is processed). By the time Header
is processed (which is processed very late), SetEnv
has set the env var.
Define
I have set the variable in my
apache2.conf
However, since you have access to the server config then consider using a "defined" variable (using the Define
directive) instead of an environment variable. Variables cannot be defined in .htaccess
(as they are set at compile time), but they can be tested for in .htaccess
and are not "renamed" like env vars (as described above).
For example, in apache2.conf
:
Define MY_SITE stage
In .htaccess
:
<IfDefine MY_SITE>
RewriteCond %{HTTP:X-Azure-FDID} !^xxxxxx$ [NC]
RewriteRule ^ - [F]
</IfDefine>
This obviously does not actually check the value of MY_SITE
, so it is assumed that it will only be set on the staging server (effectively a boolean). It would therefore be preferable to change the variable name to something like STAGING_SITE
instead.
If MY_SITE
could contain multiple values then you could check the value if you wish using the ${<varname>}
syntax. For example:
<IfDefine MY_SITE>
RewriteCond %{HTTP:X-Azure-FDID} !^xxxxxx$ [NC]
RewriteCond ${MY_SITE} =stage
RewriteRule ^ - [F]
</IfDefine>