2

I want to have all the URLs on my site handled by a single script. So I put in a rewrite rule like this:

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule (.*) /myscript.php?p=$1 [L]

But I don't want to allow access to my script on URLs that actually contain "myscript.php" in them so I would like to redirect those back to the main site:

Redirect 301 /myscript.php http://example.com/

The problem is that if I put both of those rules into my .htaccess file it causes an infinite loop. How do I get them both to work at the same time?

I would also like to be able to redirect things like:

/myscript.php?p=foo -> /foo
Stephen Ostermiller
  • 23,933
  • 14
  • 88
  • 109

2 Answers2

4

You can set an environment variable

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !myscript\.php
RewriteRule (.*) /myscript.php?p=$1 [L,E=LOOP:1]

and test for that in your second rule

RewriteCond %{ENV:REDIRECT_LOOP} !1
RewriteRule ^myscript\.php$ / [R,L]

Never test with 301 enabled, see this answer Tips for debugging .htaccess rewrite rules for details.

Community
  • 1
  • 1
Olaf Dietsche
  • 72,253
  • 8
  • 102
  • 198
1

Using an environment variable is perfectly OK, however, you don't need to manually set this environment variable yourself. Apache provides the REDIRECT_STATUS environment variable which can be used for this purpose.

REDIRECT_STATUS is empty (or not set) on the initial request. It is set to 200 on the first (successful) internal rewrite. Or some other HTTP status code in the case of an error (404 etc.).

So, instead of checking that REDIRECT_LOOP is not 1, we can simply check that REDIRECT_STATUS is empty to ensure we are testing the initial request and not the rewritten request. For example:

RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^myscript\.php$ / [R,L]

(Note that it is just REDIRECT_STATUS, there is no STATUS variable at the start of the request.)


RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !myscript\.php
RewriteRule (.*) /test/myscript.php?p=$1 [L,E=LOOP:1]

Aside: The RewriteCond directive that checks against the REQUEST_URI doesn't really do anything here. If the first condition is true (ie. it's not a file), then this condition must also be true. However, it could be optimised by including this condition first. This would then avoid the file check on every request (including the rewritten request). For example:

RewriteCond %{REQUEST_URI} !^/test/myscript\.php
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule (.*) /test/myscript.php?p=$1 [L]

Or, you could include a pre-check (an exception) before this rule instead that halts processing when myscript.php is requested:

RewriteRule ^test/myscript\.php$ - [L]

However, if you do this, then the above canonical redirects must appear before these rules, otherwise they will never be processed. (Putting the canonical redirects first is generally preferable anyway.)

MrWhite
  • 43,179
  • 8
  • 60
  • 84