1

I have the following code in my .htaccess file, which is working fine and using "pretty" permalinks to reroute everything back to /index.php, and using $_GET values to serve customized content based which URL was requested.

RewriteEngine on
RewriteBase /

RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([-\w.]+)$ index.php?page=$1 [R=302,L,QSA]

I'm just trying to get an understanding of how this code is working:

I get that the two RewriteCond's are there to avoid this behavior in the case that someone is requesting an actual file or directory. But my question involves how removing those two RewriteCond's causes a different error. If they are removed, and I request example.com/register, is the browser is redirected to:

example.com/index.php?page=index.php.

I can't see how these two things are related, so I was just wondering if anyone could explain why this happens? (I have R=302 set just for now so I can see where it's redirecting me to).

1 Answer 1

4

The pseudo-code translation of your .htaccess file would be something along these lines:

  • Line 1: In case we weren't previously planning to do anything special with URLs, we are now (RewriteEngine is an optional processing module and we're making sure its enabled).

  • Line 2: When we're talking about rewrite URLs, from here on append the path / to the beginning of the URL, which in your case means on line 6 your rewrite URL would become: /index.php?page=$1.

  • Lines 4-5:

    • %{REQUEST_FILENAME} is a special server variable containing full local filesystem path to the file or script matching the request.
    • The -d switch is a test with a boolean result value, that returns true if the preceding value is an existing directory.
    • The -f switch is a test that returns true if the preceding value is an existing file.
    • The exclamation mark (!) is equivalent to the logic word NOT.

So in this instance, lines 4-5 are checking for the current request to make sure we're not referring to an existing file or directory, and then proceeding to execute the RewriteRule on line 6. If the conditions on lines 4 and 5 are not both true, then line 6 would be skipped.

  • Line 6: The string ^([-\w.]+)$ is a regular expression:

    • A dash (-) as the very first or very last character in the square brackets will be interpreted as a literal dash and not as a range instruction.

    • The backslash and w (\w) is a shorthand character class or (see "Generic Character Types" in the PCRE Manual), where the "w" is short for "any word character" - defined in the manual as "an underscore or any character that is a letter or digit" and therefore equivalent to [A-Za-z0-9_].

    • A dot (.) inside the character class is simply a literal dot.

    • A plus (+) is one or more occurrences of whats inside the brackets [ ].

Now to summarize then on line 6:

If the request URL includes at least one occurrence of a hyphen - followed by any an alphanumeric or underscore character, then the RewriteRule will apply and change the URL to append the requested URL to index.php as a query string parameter named page for the purposes of further processing through your .htaccess file. From inside your index.php PHP script you would be able to access this value using $_GET['page'].

By specifying a 302 redirect (R=302) however, this will trigger an HTTP 302 response and redirect the user's browser to this URL rather than keeping the RewriteRule a secret internal operation.

The L tells Apache this is the last rule to follow, and the rest of the .htaccess will now be ignored.

The QSA stands for Query-String-Append and tells Apache to add the page parameter to the existing query parameters, which otherwise would be replaced by default.

For more information about understanding regular expressions, see this. You can find detailed documentation for Apache's mod_rewrite module here. And a guide with some useful examples here.

Credit to MrWhite for corrections regarding \w

4
  • Thanks for taking the time to answer! Very detailed and it explained a lot. I did know about the R=302 flag, I just had it set that way temporarily so I could see where it was being rewritten/rerouted. The links are great too, especially the last one. Thanks again.
    – Joe
    Commented Feb 7, 2014 at 16:50
  • Nicely detailed explanation (have a look at the edits for some formatting tips).
    – dan
    Commented Feb 7, 2014 at 16:52
  • ^([-\w.]+)$ - A couple corrections to the interpretation of this regex... \w is not a literal w. \w is a shorthand character class representing [A-Za-z0-9_]. Yes, this can itself be used inside a character class. And a dot (.) inside a character class is a literal dot, not any character. This regex is therefore equivalent to ^([-A-Za-z0-9_.]+)$. This means the following sentence is incorrect, "If the request URL includes -w followed by any other character" - characters inside a character class do not determine order.
    – MrWhite
    Commented Sep 1, 2017 at 23:00
  • Thanks MrWhite, I've now updated my answer taking into account your corrections. I learnt something new today :) Commented Sep 4, 2017 at 9:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.