Is there a way to get httpd RewriteRule to redirect to index.php if this exists then index.html?
Like:
RewriteEngine on
RewriteCond %{DOCUMENT_ROOT}/$1 -f [OR]
RewriteCond %{DOCUMENT_ROOT}/$1 -d
RewriteRule (.*) - [S=2]
RewriteRule (.*) /index.(php|html)?q=$1 [L]
With priority on php (if exists), then fallback to html.
if index.php:
foo.bar/test -> foo.bar/index.php?q=test
else
foo.bar/test -> foo.bar/index.html?q=test
This is a good example of the value of understanding code you copy - you already have this line, which actually does very nearly what you want:
RewriteCond %{DOCUMENT_ROOT}/$1 -f [OR]
To find out what this means, we can look up RewriteCond
in the Apache manual. It begins:
The RewriteCond directive defines a rule condition. One or more RewriteCond can precede a RewriteRule directive. The following rule is then only used if both the current state of the URI matches its pattern, and if these conditions are met.
So, this line defines an additional requirement for the following rule.
I'm going to start at the end of this rule, because it will make a bit more sense in this case. So, start with the [OR]
, which is covered under "flags":
'ornext|OR' (or next condition)
Use this to combine rule conditions with a local OR instead of the implicit AND.
So, normally a sequence of conditions all have to apply at once for the next rule to be run; but this changes it to "either this condition or the next one". For the use case you're looking for, we only need one condition: "does index.php exist?" so this flag won't be relevant.
The next thing to look at is the -f
, which is listed under the heading "You can perform various file attribute tests":
-f
Is regular file.
Treats theTestString
as a pathname and tests whether or not it exists, and is a regular file.
Great, so this condition is about what file exists, which is what we want! But what's going on with TestString
, and what does %{DOCUMENT_ROOT}/$1
mean? Let's go back to the beginning:
Syntax: RewriteCond TestString CondPattern [flags] ... TestString is a string which can contain the following expanded constructs in addition to plain text: ...
So our "TestString" is the %{DOCUMENT_ROOT}/$1
. Looking for the parts it contains, we find:
Server-Variables: These are variables of the form %{ NAME_OF_VARIABLE }
There isn't a detailed description of DOCUMENT_ROOT
just here, but there is a link to where "most are documented", which in turn takes us to this DocumentRoot
manual. Long story short, it's the root of the project which your URLs are going to be relative to:
The server appends the path from the requested URL to the document root to make the path to the document
This makes sense - Apache can't look for "hello-world.txt" unless it knows which directory to look in, so the condition is making that explicit.
Now back to RewriteCond
to look up $1
:
$1 to $9 provide access to the grouped parts (in parentheses) of the pattern, from the RewriteRule which is subject to the current set of RewriteCond conditions.
In the particular condition we were looking at, the file to look up is dynamic, based on the URL coming in - the condition is essentially "does the requested file exist. In our new condition, we don't need that - we're just saying "does index.php exist", so we can hard-code "index.php" in the rule.
The other part you have almost there already is the rewrite rule. If we take out the (php|html)
part, which won't work, we can have this:
RewriteRule (.*) /index.php?q=$1 [L]
Without going into too many details, the RewriteRule
syntax is fairly straight-forward: the first argument is a pattern to match against the URL the browser requested; the second argument is what you want it to be mapped to internally.
The only extra part that's happening here is that $1
refers to the string that was matched, like it did in the RewriteCond
above. In this case, we're just "capturing" the whole requested URL, so if the requested URL was hello-world
, then $1
is substituted with hello-world
, and the final URL is /index.php?q=hello-world
Coming back to the answer, you mocked up this pseudo-code:
if index.php:
foo.bar/test -> foo.bar/index.php?q=test
else
foo.bar/test -> foo.bar/index.html?q=test
We can reword that slightly to be closer to Apache syntax:
condition: if index.php exists in the document root
rule: match /test and serve /index.php?q=test
else
rule: match /test and serve /index.html?q=test
The only part of that we haven't just covered is the "else". There are a few ways to do that:
[END]
flag on the first rule, to say "once you've done this, don't look any further"[S=1]
flag, which another of your existing rules already uses, to skip over just the second rule then carry on processing