Using FallbackResource with Apache Header Expressions Causes Weirdness
Recently we needed to set one of our Apache servers to output a HTTP header for a specific URL, a simple task right? Well it turns out it can be a right pain...
The server is pretty standard, it's running Apache 2.4.43 passing requests to a PHP application.
Apache has in built functionality to conditionally set headers it's as simple as:
header set X-Header-Name "Header Value" "expr=%{REQUEST_URI} =~ m#^/matching/uri$#"
This simply put this says output a header named X-Header-Name
with the value "Header Value"
only if the requested URI matches the regex ^/matching/uri$
. This didn't nothing though, I changed the regex and replaced it with a straight ==
but the only time I could get it to match and set a header was with broad patterns like .*
or ^/.+
.
Next I wanted to know what Apache thought the value of %{REQUEST_URI}
was, clearly it was matching against something but maybe mod_rewrite was changing something. So I got Apache to send a header, non conditionally, which contained the value of %{REQUEST_URI}
so I could see what it was:
Header set X-Request-URI "%{REQUEST_URI}e"
This returned exactly what I expected the.. requested URI, this made no sense! At this point I started looking at alternatives to %{REQUEST_URI}
, and found that I could match against %{THE_REQUEST}
but this felt like a work around that probably had issues I hadn't yet run into.
In an attempt to reduce the number of variables I disabled mod_rewrite, this made no difference, so that can be ruled out.
It was as I was putting the kettle on for the third time that something occurred to me. Even with mod_rewrite disabled the application was still being passed requests. And all requests are passed to index.php
: low and behold the regex ^/index.php$
matches. This makes for some confusing possibilities. For example with the following code a request to /matching/uri
will set the X-Request-URI
header:
Header set X-Request-URI "%{REQUEST_URI}e"
So what this all boils down to is that %{REQUEST_URI}
!= %{REQUEST_URI}e
which kind of makes sense (though I'm not exactly sure what the difference is). In an attempt to get the right value I turned my attention to the env
function because I knew that the e in %{REQUEST_URI}e
stands for environment.
Header set X-Request-URI "%{REQUEST_URI}e"
This didn't work either, it turns out that when using an <if>
env('REQUEST_URI')
!= %{REQUEST_URI}e
however this isn't the case when using an inline expression:
Header set X-Header-Name "Header Value" "expr=env('REQUEST_URI'') =~ m#^/matching/uri$#"
This finally works! The docs allude to
When environment variables are looked up within an <
If
> condition, it's important to consider how extremely early in request processing that this resolution occurs.
I am no expert on the order of Apaches request processing so I'll leave working that one out as an exercise to the reader
Conclusion
Apache is weird.
If you are using mod_dir's FallbackResource
to pass requests to your application and want to access REQUEST_URI
in expressions (which now I write it sounds really niche) you need to use env('REQUEST_URI')
otherwise you will get the value of the application entry point (eg index.php
)