Forum Moderators: phranque

Message Too Old, No Replies

Hiding php extension

         

Lomacar

10:31 pm on Apr 29, 2010 (gmt 0)

10+ Year Member



NOTE: In the process of writing this and testing some things I realized that I don't really need to do this but for the sake of curiosity and the fact that I wasted so much time on it I would like to know if/how it could be done.

This is an age old problem right? But for some reason I can't find a solution to my particular situation. I have a site with php files that all link to each other and
header(Location:)
each other etc. I would like to not have to change all the existing file references. I would like attempts to load /xyz.php to be Redirect-ed to simply /xyz, which doesn't actually exist but then to use a RewriteRule to point it back to the /xyz.php. I can do the redirect or rewrite seperately but when I put them together it seems to cause an infinite loop.

Again, out of curiosity, how bad would it be for performance to constantly redirect all php files like this? All I really want to achieve is hiding the php extension in the browser but ideally use the php extension when refering to files internally. Maybe there is a better way.

I realize now this was a silly idea. HTML links should point to the non-.php URL to look consistent and I can still refer to the .php URL just fine in the PHP code, in fact
include()
demands the extension. The only thing left to change, as far as I can think of, is
header(Location:)
calls. It would be kind of nice to have my PHP code consistently use the .php extension, but whatever.

So for the .htaccess code this will suffice, right?
Options +FollowSymlinks
RewriteEngine on
RewriteRule ^([^.]+)$ $1.php

[edited by: bill at 1:15 am (utc) on Apr 30, 2010]
[edit reason] turned off smilies [/edit]

lammert

11:10 pm on Apr 29, 2010 (gmt 0)

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



Hi Lomacar, first of all welcome to WebmasterWorld!

Hiding the file extension in a visible URL is something which is recommended by Tim Berners-Lee, the inventor of the Internet. So you are in good company :)

Beside what you propose with rewrite rules, there is another way to do this and that is by switching on the Content Negotiation option in Apache. Content Negotiation let the Apache server decide which file to serve when an URL is entered without an extension. Normally it is used to serve content in a language which is understood by the visitor, or serve compressed content to some browsers and uncompressed to others. Content negotiation decides which source file to take based on headers sent by the browser to the web server.

Content negotiation is switched on with the setting Options MultiViews in the central httpd.conf file or in an .htaccess file. When used in the central httpd.conf file, the setting must be in a <Directory>, <Location> or <Files> section.

g1smd

11:20 pm on Apr 29, 2010 (gmt 0)

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



It is links that define URLs, so if users are to see extensionless URLs, that is what you must link to.

The rewrite is easy. It connects an HTTP request for an extensionless URL arriving at the server, to the correct filename (with extension) inside the server.

The redirect is also quite simple. It needs to fire only for direct client requests for URLs that include the .php extension. So, add a RewriteCond before the RewriteRule and get it to examine THE_REQUEST for URL request ending in .php here.

This is a question that comes up almost every week (the two redirect/rewrite rules), so there is a lot of example code in prior threads. There's at least several already this month.

Lomacar

8:47 pm on Apr 30, 2010 (gmt 0)

10+ Year Member



Thanks guys.

g1smd: I'm not sure if what you are saying works. At least I don't understand it. Perhaps you could post some code. It seems to me I need a condition on the redirect. I think what happens is if you request "file.php" it redirect to "file" then the rewrite rule points it back to "file.php" which then unfortunately retriggers the redirect. If I could somehow make the redirect not happen after the rewrite that would be dandy.

I scoured the Internet and this forum for how to do this and found many similar things but not a solution for my exact case.

jdMorgan

8:59 pm on Apr 30, 2010 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



The method g1smd posted is one of two methods that do work.

Read what he wrote again, paying attention to the details... The RewriteCond is used to make sure that the request for "file.php" is coming direct from a client, and is not the result of the previously-invoked file->file.php rewrite inside the server.

The other method, also using a RewriteCond, is to check the REDIRECT_STATUS variable.

Both methods prevent the loop you describe, and many examples of both may be found here using the major search engines' "site search" features or our own site search function (link at top of this page).

Jim

g1smd

9:28 pm on Apr 30, 2010 (gmt 0)

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



found many similar things but not a solution for my exact case.


It's highly unlikely you'll find an "exact" match - but there are many examples here with just slightly different filenames. Use those as a basis for writing your own code.

Lomacar

12:22 am on May 1, 2010 (gmt 0)

10+ Year Member



Well I can't get it to work and I can't discover what to do by looking at other posts or websites or the Apache documentation. This doesn't work, it causes endless redirects.

Options +FollowSymlinks

RedirectMatch (.*)\.php $1

RewriteEngine on
RewriteCond %{REQUEST_URI} !\.php$
RewriteRule ^([^.]+)$ $1.php


If I take out the ! in the RewriteCond then the rewrite rule simply never happens.

physics

1:07 am on May 1, 2010 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



It's an infinite loop because here's what you're telling the server (I think)

1) if you see a php page like foo.php, please redirect it to foo

2) if you see a non php page (this is the !\.php$), please rewrite it to foo.php

So it goes 1/2/1/2/1/2...

One idea would be to do the following.

For every php file, make a directory with the same name, then copy the php file into that directory and name it index.php (also, never again name a web page anything other than index.php or index.whateverlanguage)

So foo.php becomes foo/index.php

Then redirect any requests for foo.php to foo

jdMorgan

2:34 am on May 1, 2010 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



So here goes the 834th example of this code to be indexed by Googlebot:

Options +FollowSymLinks -Indexes -MultiViews
RewriteEngine on
#
# Externally redirect [i]only[/i] direct client requests for /<anything>.php
# in any directory to /<anything> in that same directory
RewriteCond %{THE_REQUEST} ^[A-Z]+\ /([^/]+/)*[^.]+\.php\ HTTP/
RewriteRule ^(([^/]+/)*[^.]+)\.php$ http://www.example.com/$1 [R=301,L]
#
# Internally rewrite requests for extensionless URL-paths to append ".php"
RewriteRule ^(([^/]+/)*[^./]+)$ /$1.php [L]

The above code will rewrite requests for the extensionless URL-path /xyz to filepath /xyz.php, and will 301-redirect direct client requests for URL-path /xyz.php to URL http://www.example.com/xyz

It will do this correctly no matter what directory is requested, and it will not loop.

Jim

Lomacar

7:56 am on May 2, 2010 (gmt 0)

10+ Year Member



Thanks for your patience with me.

Well that is interesting. I did see something like that in another thread but I never imagined it was actually for what I wanted. That thing doesn't use any Redirect, only RewriteRule. I thought the effect of RewriteRule was to point to files "behind the scenes" without actually changing the address in the browser. I guess it is that R=301 option that does it. The Apache documentation is really confusing about what that does.

I tried out your code but it had one shortcoming, it didn't handle query strings or anchors. I changed the RewriteCond line to:

RewriteCond %{THE_REQUEST} ^[A-Z]+\ /([^/]+/)*[^.]+\.php(.*)?\ HTTP/


(Just added the (.*)? near the end.) That seemed to work fine. Do you foresee any problems with that?

There still is a lot going on there that I don't understand. I was hoping to learn a bit more in coming to this solution. -Indexes is irrelevant to the solution isn't it? On the other hand in studying MultiViews it looks like I could actually use that in place of the second RewriteRule. Why is it -MultiViews and not + ? If I use MultiViews instead of the RewriteRule then I have to be careful about different file types with the same name right? The RewriteCond clearly deals with features of Apache and the Internet that I don't know much about. Where do you go to school to learn that? Not that I have gone to school at all for web development...

If the second RewriteRule does turn out to be necessary can't it be simplified to just this?
RewriteRule ^([^.]+)$ $1.php
Do I lose something doing it this way?

g1smd

9:00 am on May 2, 2010 (gmt 0)

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



RewriteRule does rewrites (and needs the [L] flag), and does redirects when the target contains a domain name and/or the [R=301,L] flag is added. If you include the domain name and omit the R flag, or you just use [R] with no number, then you get a 302 redirect.

The (.*) pattern is "greedy and ambiguous". You're much better off with ([^\ ]+)? or ([^\ ]*) which means anything that is not a space. It can be read in one left to right pass, instead of needing very many "back off and retry" attempts.

The second rule tests for URL requests for pages without extensions. Your rule would rewrite a request for /robots.txt such that you'd need a file called /robots.txt.php to fulfill the request.

Beware that a target of $1.php allows the user to inject whatever path information they want. You must use /$1.php with a leading slash to mitigate this security risk.

I did all my learning in this forum and using the official Apache docs. Just about every topic has been covered multiple times before.

jdMorgan

4:24 pm on May 2, 2010 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



"Shortcomings" are to be expected unless stated requirements are comprehensive...

For query strings and named anchors, I'd suggest:

RewriteCond %{THE_REQUEST} ^[A-Z]+\ /([^/]+/)*[^.]+\.php([?#][^\ ]*)?\ HTTP/

Jim

Lomacar

6:00 pm on May 3, 2010 (gmt 0)

10+ Year Member



Thanks for the tips and explanations. I just don't understand one thing that g1smd said. Why would robots.txt get rewritten with my rule? robots.txt has an extension, or more specifically a dot, so it shouldn't get rewritten according to my rule. What advantage does
^(([^/]+/)*[^./]+)$
have over
^([^.]+)$
?

g1smd

6:07 pm on May 3, 2010 (gmt 0)

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



I misread your rule.

You're right; it would not affect robots.txt requests at all.

The alternative rule jumps a folder level at a time before looking at the filename part of the request.

jdMorgan

1:18 pm on May 4, 2010 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



... it is therefore more efficient, and allows periods to be used in directory names without malfunctioning.

Jim