I agree with Walf in that handling routes through a router class is a better idea (especially in the long run!) than using .htaccess
redirects.
However, as your question seems to be more about why is this not working than about how you should do it, here is an explanation for what is going on.
I will be using these URLs as examples:
localhost:8080
localhost:8080/app
localhost:8080/app/customers/id/5
Your first rule:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{HTTP_HOST} ^(www.)?localhost:8080$
RewriteRule ^(.*)$ /views/$1
As you intended, this RewriteRule will match any URL which is not a file, not a directory, and made to localhost:8080.
localhost:8080 # not matched because it leads to a directory.
localhost:8080/app -> localhost:8080/views/app
localhost:8080/app/customers/id/5 -> localhost:8080/views/app/customers/id/5
Your next rule:
RewriteRule ^(/)?$ /views/index.php [L]
It is important to realize that RewriteCond statements apply only to the first RewriteRule following them, thus all that is being checked here is the path.
Side note: ^(/)?$
, as you are not using $1
, can be simplified to ^/?$
.
localhost:8080 -> localhost:8080/views/index.php
localhost:8080/views/app # not matched
localhost:8080/views/app/customers/id/5 # not matched
As the L
flag is specified, Apache will immediately stop the current iteration and start matching again from the top. The documentation is badly worded. Thus, localhost:8080/views/index.php
will be run through the first rule, fail to match, be run through this rule, fail to match, and then as no other rules exist to check (yet) no rewrite will be done.
Now lets look at what happens when you add your broken rule.
RewriteRule /id/(.*) customers.php?id=$1
There are a few problems here. First, as you don't require that the URL start with /id/
the rule will always match a URL that contains /id/
, even if you have already rewritten the URL. If you amended this by using ^/id/(.*)
, then you would still have issues as the string that the rewrite RegEx is tested against has leading slashes removed. Lastly and most importantly, customers.php
does not exist in your root directory.
localhost:8080/views/index.php # not matched
localhost:8080/views/app # not matched
localhost:8080/views/app/customers/id/5 -> localhost:8080/customers.php?id=5
This is the last rule in your file currently, so now Apache will start over. customers.php
does not exist in your directory, so it will be rewritten to views/customers.php
. No other rules matched, but the URL has changed and so Apache will start over again, as /views/customers.php
does not exist, it will be rewritten to /views/views/customers.php
... This pattern will repeat until you hit the maximum iteration limit and Apache responds with a 500 error.
You can solve this several ways. Here would be my preferred method, but only if you cannot use a router.
RewriteEngine on
# Rewrite the main page, even though it is a directory
RewriteRule ^/?$ views/index.php [END]
# Don't rewrite any existing files or directories
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule .? - [S=999,END]
RewriteRule ^app/?$ views/app/index.php [END]
RewriteRule ^app/id/(.*)$ views/app/customers.php?id=$1 [END]
TL;DR Use a PHP based router. .htaccess
rules can be incredibly confusing.