Reputation:
When performing rewrites with mod_rewrite's RewriteRule
, the substitution is evaluated and a guess made as to whether it's a URL or a file-system path, based on whether the root directory of the substitution exists on the file system. Here's the relevant section from the documentation:
URL-path
Note that mod_rewrite tries to guess whether you have specified a file-system path or a URL-path by checking to see if the first segment of the path exists at the root of the file-system. For example, if you specify a Substitution string of
/www/file.html
, then this will be treated as a URL-path unless a directory namedwww
exists at the root or your file-system (or, in the case of using rewrites in a.htaccess
file, relative to your document root), in which case it will be treated as a file-system path.
So my question is, how do I rewrite to a URI, where the root directory does exist in the file-system, but I want it to be treated as a URL?
Is there any way other than specifying the full URL? Also according to the docs:
Absolute URL
If an absolute URL is specified, mod_rewrite checks to see whether the hostname matches the current host. If it does, the scheme and hostname are stripped out and the resulting path is treated as a URL-path. Otherwise, an external redirect is performed for the given URL.
So I can get round it like this:
RewriteRule ^/example.html$ %{REQUEST_SCHEME}://%{HTTP_HOST}/var/example.html
Which is not ideal for readability of the code. I really want to be able to do:
RewriteRule ^/example.html$ /var/example.html
Without that being evaluated as a file-system path when /var
exists. There doesn't seem to be a flag to force this. There is also a risk of a matching path being created later and breaking rules, which I would like to avoid without so much verbosity.
I am also aware I can solve this by putting the rules in .htaccess
instead of the Apache config, so the search is made from the document root as described in the quote above, but that's not what I'm looking for.
Is there any other way?
Update
Thanks to Dusan Bajic's answer, it turns out the [PT]
flag can help here, as hinted at in the sentence I missed off the first quote from the docs:
If you wish other URL-mapping directives (such as Alias) to be applied to the resulting URL-path, use the [PT] flag as described below.
By using the [PT]
flag (docs here), it will cause the substitution "to be treated as a URI instead [of a file-path]", which sounds like it should answer this question, but unfortunately there are other implications to using the [PT]
flag:
[L]
so no further processing of rewrite rules will occur.So I am leaving this question open without an accepted answer for now, as the desired solution will not have the above issues. An accepted answer will cause the substitution to be treated as a URL-path, as it would be if the root directory of the substitution did not exist in the file-system, without affecting the processing in any other way.
Update 2
I realised that another option to help with this, when the URL is within the document root, is to always add %{DOCUMENT_ROOT}
at the start of the substitution. So it makes it like this:
RewriteRule ^/example.html$ %{DOCUMENT_ROOT}/var/example.html
This also prevents later rules that process the URL from working though, so it's similar to the [PT]
option without causing the URL mapping to run again.
Upvotes: 8
Views: 2986
Reputation: 10849
You overlooked the last sentence of the quoted paragraph:
If you wish other URL-mapping directives (such as Alias) to be applied to the resulting URL-path, use the [PT] flag as described below.
It seems like it does not apply to your (very sensible btw.) question, but if you dig further into [PT]:
The target (or substitution string) in a RewriteRule is assumed to be a file path, by default. The use of the [PT] flag causes it to be treated as a URI instead.
For example (if you turn on LogLevel debug rewrite:trace6
):
RewriteRule ^/boo$ /opt/mock.png
Will log:
[...] init rewrite engine with requested uri /boo
[...] applying pattern '^/boo$' to uri '/boo'
[...] rewrite '/boo' -> '/opt/mock.png'
[...] local path result: /opt/mock.png
[...] go-ahead with /opt/mock.png [OK]
but
RewriteRule ^/boo$ /opt/mock.png [PT]
Will log:
[...] init rewrite engine with requested uri /boo
[...] applying pattern '^/boo$' to uri '/boo'
[...] rewrite '/boo' -> '/opt/mock.png'
[...] forcing '/opt/mock.png' to get passed through to next API URI-to-filename handler
Upvotes: 4