Forum Moderators: phranque

Message Too Old, No Replies

Redirect trailing slash on extensionless URLs

I'm trying to serve extensionless .php urls with redirects

         

ryanve

3:02 am on Jun 24, 2011 (gmt 0)

10+ Year Member



I'm trying to serve extensionless .php urls with redirects to remove trailing slashes and the .php

example that works:

ryanvanetten.com/resources/lame/
and
ryanvanetten.com/resources/lame.php
both redirect to
ryanvanetten.com/resources/lame
where the actual file served is lame.php

broken example:

airve.com/test/file
properly serves
file.php

but the redirects
airve.com/test/file/
and
airve.com/test/file.php
don't work. (Try them—they seem redirect to an internal absolute path.)

In both cases:

I'm using the exact same .htaccess file with only the code below, and both domains are hosted on Dreamhost.

RewriteEngine on

#unless directory, remove trailing slash
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)/$ $1 [R=301,L]

#redirect external .php requests to extensionless url
RewriteCond %{THE_REQUEST} ^(.*)\.php([#?][^\ ]*)?\ HTTP/
RewriteRule ^(.*)\.php$ $1 [R=301,L]

#resolve .php file for extensionless php urls
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteRule ^(.*)$ $1.php [L]

I've also tried it with and without +Multiviews and -Multiviews, but no dice. I'm thinking there must be some difference in the Apache settings on the second example, which is a newer domain. Any ideas how to get this working?

g1smd

6:23 am on Jun 24, 2011 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



The target URL in both of the redirects should specify the protocol and domain too, not just $1.

Only use (.*) when it is the LAST item in a pattern.

For
^(.*)/$
use
^([^/]+/)+$
here.

You'll also need to replace the (.*) in the next two rules with a better pattern.

The last rule is over complicated. Remove the -f and -d checks and use
^([^/]+/)*([^/.]+)$
or
^([^/.]+)$
as the RegEx pattern to detect "extensionless" requests.

In the last rule, add a slash before $1.php to stop a malformed request taking over your server.

ryanve

6:14 pm on Jun 24, 2011 (gmt 0)

10+ Year Member



Nice—thank you—that definitely helps. I have it partially working now.

The code below successfully serves airve.com/test_file and and redirects requests w/ the extension but it causes airve.com/test_file/ to redirect to airve.com and it doesn't work at all in the subfolder airve.com/test/file

#unless directory, remove trailing slash
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^/]+/)+$
http://
airve.com/$1 [R=301,L]

#redirect external .php requests to extensionless url
RewriteCond %{THE_REQUEST} ^(.+)\.php([#?][^\ ]*)?\ HTTP/
RewriteRule ^(.+)\.php$
http://
airve.com/$1 [R=301,L]

#resolve .php file for extensionless php urls
RewriteRule ^([^/]+/)*([^/.]+)$ $2.php [L]

(Adding the slash as suggested in the third rule before $2.php in the last rule caused a not found error.)

Then I tried placing an .htaccess file in the subfolder /test/ itself with the code below, which works except that it redirects everything to airve.com/test//file - is there a way to prevent that extra slash?

#unless directory, remove trailing slash
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^/]+)/$
http://
airve.com/test/$1 [R=301,L]

#redirect external .php requests to extensionless url
RewriteCond %{THE_REQUEST} ^(.+)\.php([#?][^\ ]*)?\ HTTP/
RewriteRule ^(.+)\.php$
http://
airve.com/test/$1 [R=301,L]

#resolve .php file for extensionless php urls
RewriteRule ^([^/.]+)$ $1.php [L]

lucy24

6:35 pm on Jun 24, 2011 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



it redirects everything to airve.com/test//file - is there a way to prevent that extra slash?

If it's supplying a slash in this location, what happens if you withhold one from your end? Either

RewriteRule ^/(.+)\.php$ http://example.com/test/$1 [R=301,L]
or
RewriteRule ^(.+)\.php$ http://example.com/test$1 [R=301,L]

I also kinda think you've got the rules backward, so everything will be redirected twice, but don't quote me ;)

ryanve

6:50 pm on Jun 24, 2011 (gmt 0)

10+ Year Member



Removing the slash like that was my first reaction too, but it had no effect (either method).

I tried rearranging the order too—no difference. Thanks—if I figure it out I'll post it here.

g1smd

8:36 pm on Jun 24, 2011 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



For ^(.*)/$ use ^([^/]+/)+$ here.

Ack! Typo alert. I meant
^([^/]+)/$
if there are no folder levels.
If there are multiple folder levels use
^(([^/]+/)*([^/.]+))/$
instead.

The (.+) pattern is still no better than (.*) and not appropriate.

In the last rule, add a slash before $1.php to stop a malformed request taking over your server; use /$1.php here.

ryanve

9:22 pm on Jun 24, 2011 (gmt 0)

10+ Year Member



Thanks again—I removed that extra plus sign but the first rule still causes airve.com/test_file/ to redirect to the homepage.

Using ^(([^/]+/)*([^/.]+))/$ causes airve.com/test/file and airve.com/test/file/ to both redirect to airve.com/test//file (but at least it serves the file)

In the last rule, add a slash before $1.php to stop a malformed request taking over your server; use /$1.php here.

When I did that I got a 'not found' error.

The (.+) pattern is still no better than (.*) and not appropriate.

What do you mean by not appropriate? The 2nd rule seems like it's working fine.

g1smd

11:12 pm on Jun 24, 2011 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



(.+) and (.*) read the "entire" pattern into the $1 backreference and then the parser finds out there is more stuff after that which means you didn't mean to say "all" here. It then has to invoke error routines to "back off and retry" until it finds out what you actually meant.

A different pattern, one which can be parsed left-to-right in one pass, will be much more efficient.

ryanve

12:11 am on Jun 25, 2011 (gmt 0)

10+ Year Member



Ahhhh—thanks for explaining ;)