user2493235
user2493235

Reputation:

Force a URL-path for mod_rewrite's RewriteRule Substitution

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 named www 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:

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

Answers (1)

Dusan Bajic
Dusan Bajic

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

Related Questions