3

I'm having this very annoying problem with my rewrite rules in the .htaccess file.

The context

So what I want is to have these two types of URLs rewrite to different targets:

URL 1 -- http://example.com/rem/call/answer/{Hex String}/{Hex String}/
URL 2 -- http://example.com/answer/{Hex String}/{Hex String}/

This is an extract of my .htaccess file:

RewriteEngine On
RewriteRule rem/call/answer/([a-f0-9]+)/([a-f0-9]+)/?$ /TARGET1
RewriteRule answer/([a-f0-9]+)/([a-f0-9]+)/?$ /TARGET2

The problem

Now the problem is that URL 2 rewrites well (using rule #2) and goes to TARGET 2, but URL 1 rewrites with both rules instead of just rule #1.

I tried several solutions, including the obvious use of the character ^ for "start of string". At that point, my rewrite rules were:

RewriteEngine On
RewriteRule rem/call/answer/([a-f0-9]+)/([a-f0-9]+)/?$ /TARGET1
RewriteRule ^answer/([a-f0-9]+)/([a-f0-9]+)/?$ /TARGET2

However, another problem happened. This time it's URL 1 that rewrites well, with only rule #1 and goes to TARGET 1. But now URL 2 doesn't rewrite at all any more. I'm guessing it's because the second rewrite rule never matches any url and thus never applies.

The only solution I found so far is to remove the ^ and use the [L] flag at the end of rule #1 like so:

RewriteEngine On
RewriteRule rem/call/answer/([a-f0-9]+)/([a-f0-9]+)/?$ /TARGET1 [L]
RewriteRule answer/([a-f0-9]+)/([a-f0-9]+)/?$ /TARGET2

This way, it uses rule #1, matches, but never gets to rule #2. Both urls get rewritten properly with these rules, but it is not a good solution since I might not want to stop the rewriting of URL 1 after the first rule applies (what if I have a third rule I would want to apply to it as well...)


My questions to you

Now that I've stated the problem, my questions here are:

  1. Is the [L] flag the only way to go ? (which I highly doubt, and certainly hope not)
  2. Would ^ be a candidate solution ? (I think so)
  3. If so, how to make it work and why is it not working at all in my case ?

What I suspect

I suspect that it has something to do with the fact that the URL is actually http://example.com/answer/{Hex String}/{Hex String}/ and not just answer/{Hex String}/{Hex String}/, which means that answer/.. isn't really at the beginning of the string and thus prefixing it with ^ doesn't work.

But then it brings me to another question:

How to tell apache to strip the url of the scheme+domain part (i.e. http://example.com/) and match rules with the remainder of the url only (e.g. answer/{Hex String}/{Hex String}/) ?


EDIT

I should also add that I've tried the basic alice-bob example. I have a file named bob.html in my root and the following rule in my .htaccess file:

RewriteRule alice.html$ /bob.html

This works just fine and displays the bob.html page when alice.html is queried. However, if I change the rule to:

RewriteRule ^alice.html$ /bob.html

I then get a 404 error when querying the alice.html page...

As for @anubhava's comment, my full .htaccess file is composed as follows:

RewriteEngine On

[A bunch of RewriteRule that have nothing to do with the topic at hand
(don't contain any "answer" string in them and all work perfectly)]

RewriteRule rem/call/answer/([a-f0-9]+)/([a-f0-9]+)/?$ /TARGET1 [L]
RewriteRule answer/([a-f0-9]+)/([a-f0-9]+)/?$ /TARGET2

ErrorDocument 404 /404.html
Header set Access-Control-Allow-Origin "*"
SetEnv file_uploads On
Community
  • 1
  • 1
Dolma
  • 394
  • 2
  • 9
  • 1
    out of curiosity, does the TARGETs have an "answer" in them? – Jon Lin Sep 03 '15 at 18:36
  • Hmm ... what do you mean by "answer" ? – Dolma Sep 03 '15 at 19:28
  • Does it have the string `answer` in it. FOr example: `RewriteRule ^answer/(.*)$ /some/path/answer/foo.php`? Does the string "answer" appear in your rule's target? – Jon Lin Sep 03 '15 at 19:37
  • Yes it does, it redirects to an "answer.php" page with the two captured hex codes in the query string. Btw, I edited my question to add a detail, which makes me believe this is not the problem (or just one of them). – Dolma Sep 03 '15 at 19:41
  • @JonLin Actually, I stand corrected, I was confusing it with another rule. The first rule actually doesn't have any `"answer"` in it. And the second one has something like `"redirect-answer.php?..."`. So I really think the problem comes from somewhere else. Where you thinking of some looped rewriting ? – Dolma Sep 03 '15 at 19:47
  • @anubhava I edited my question to add the structure of my full .htaccess file – Dolma Sep 03 '15 at 20:35
  • @anubhava Yes, as I mentioned to Jon Lin, rule #2 I mention in the post rewrites to `redirect-answer.php` (target 2). And the `.htaccess` file is located in the root directory above my `www` directory. It still bothers me as to why even the simple alice-bob test didn't work... – Dolma Sep 03 '15 at 20:53
  • 1
    Yes `RewriteRule ^alice.html$ /bob.html` should work. Should it not be in `www` directory? – anubhava Sep 03 '15 at 20:58
  • @anubhava Ughh ... you're right, it should be ... I recall I checked a long time ago when I first set up my `.htaccess` file and I read online that I should put it above the `www`. Everything worked well until now so I didn't really question it. I just tried moving it to the `www` directory and the `^` now works like a charm. I feel really stupid ^^ Well, problem solved, thanks a lot ! – Dolma Sep 03 '15 at 21:16

1 Answers1

2

Ok so, thanks to @anubhava's comments, I solved the problem easily by moving the .htaccess file down one level to the www directory.

I was still quite curious about why this solved my problem, so I went on investigating how apache's rewriting works. I'm not sure I've got all the details right, but here's what I found out.

Location location location

Of course, it goes without saying that the location of files is important, and especially configuration files like .htaccess. But it goes even beyond simple file path, and here is the reason why:

  1. First, you need to keep in mind that the .htaccess file will affect the directory it's located in as well as all its subdirectories. So it would seem logical that a global .htaccess file should be placed at the root directory of your website, since it will affect all subdirectories (i.e. the whole website).

  2. The second thing to keep in mind is that the public_html directory (which in my case was called www, simply a symbolic link to public_html) is the root folder of your website's content. You might have access to its parent directories, but whatever you put outside of your public_html directory is not part of your website's content per se, any resource you put there won't be part of your website's hierarchy (i.e not accessible via http://example.com/path/to/resource).

  3. The regex option ^ matches the start of a string, here in the context of URL rewriting, it's the start of the considered URL. And that's not all, it seems that Apaches resolves matches relatively to the location of your .htaccess file. Which means that the ^ not only references the start of the string you wrote as part of the rule but actually references it relatively to the actual path of the .htaccess file which acts as a "local root directory" for all the rewrite rules in that specific .htaccess file.


Example

Let's say you have a subdirectory (e.g http://example.com/sub/directory/) and inside it you have two files:

http://example.com/sub/directory/.htaccess
http://example.com/sub/directory/bob.html

inside this .htaccess file, you have a rewrite rule as follows:

RewriteRule ^tom.html$ /sub/directory/bob.html

This rule will not match http://example.com/tom.html as you could expect the ^ to act, but instead will match http://example.com/sub/directory/tom.html since this is where the .htaccess file is located.


Conclusion

Generally speaking, let's say you have a rewrite rule such as:

RewriteRule ^PATH$ /TARGET_PATH

This means that the rule will not match the URL against ^PATH$, but instead it actually matches it against ^[Location of the .htaccess file]/PATH$

In other words, the location of the .htaccess file acts as a sort of base URL for all rewrite rules in it (much similar to the base tag in html).

This is why my rewrite rule with ^ didn't work, since my .htaccess file was located above the public_html directory, and that parent directory was acting as the base URL for my rules. Thus the rule would never match any URL since it would compare it with a path never accessed (because above the website's content root).

I hope this was clear enough to help anyone who might encounter the same problem I had.

Cheers

Dolma
  • 394
  • 2
  • 9