I would like a to do a rewrite rule that removes all extensions - regardless of filename
https://example.com/filename.extension -> https://example.com/filename
for example:
https://example.com/horses.txt -> https://example.com/horses
https://example.com/icecream.json -> https://example.com/icecream
I tried:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^(.*)\.*$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.+)$ *? [QSA,L]
</IfModule>
not working as it should
You can only reasonably do what you are asking with MultiViews.
For example, as simple as:
Options +MultiViews
You need to remove your existing mod_rewrite directives.
Now, a request for example.com/horses will be correctly routed to /horses.txt, or whatever file extensions you are using. MultiViews uses mod_negotiation.
This isn't so easy to do with mod_rewrite, since you need to test each file extension in turn in order to work out what file you need to rewrite back to in order to route the request correctly. eg. Should a request for example.com/horses route to /horses.txt or horses.jpg? MultiViews does this comparison for you.
I would like a to do a rewrite rule that removes all extensions
Although, you need to actually remove the file extension in the HTML source. This isn't something you do in .htaccess, unless you need to preserve SEO or backlinks that have already linked back to the old URLs.
UPDATE: Perhaps I wasn't clear enough, I would like the url to display without the extension even if it is linked to it, or to go to that file if linked without the extension
Well, you need to actually remove the file extension on all your internal links. You can issue a "redirect" in .htaccess to remove the extension for the benefit of search engines and 3rd party links - but if you rely on this for your internal links then it will potentially slow users and your site as you are doubling the number of requests hitting your server.
To remove the file extension for direct requests (SEO / 3rd party links), you could do something like this:
RewriteEngine On
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^([^.]+)\.[\w]{2,4}$ /$1 [R=302,L]
This does assume that the only dot in the URL-path is the one that delimits the file extension.
The difficult part is then internally rewriting the request back to the underlying file with an extension - that's where MultiViews comes in (first part of my answer).
Related
I am trying to set up pretty urls with .htaccess.
I got it to work, so both domain.com/contact/ works, and domain.com/contact.php still also works. But now there are 2 urls in my domain, with the same content? Can this be avoided? So only domain.com/contact/ will work?
My .htaccess file looks like this:
Options +FollowSymLinks
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([^.]+)([^./])$ %{REQUEST_URI}/ [L,R=301,NE]
RewriteRule ^contact/$ /contact.php
RewriteRule ^contact/$ /contact.php
You can either block direct access to /contact.php or redirect /contact.php back to /contact/ (the canonical URL). The later is generally preferred. For example:
RewriteRule ^contact\.php$ /contact/ [R=301,L]
RewriteRule ^contact/$ contact.php [END]
Note the addition of the END flag (Apache 2.4+) on the existing rewrite to stop all further processing (preventing a redirect loop). I've also removed the slash prefix on the substitution string (preferable for internal rewrites).
Since you are rewriting to a file with the same basename as in the requested URL, you need to also make sure that MultiViews (part of mod_negotiation) is disabled, otherwise /contact (no slash) will also "work" and you could run into issues later (if you want to do more complex rewrites). Alter the Options directive at the top of the file like so:
Options +FollowSymLinks -MultiViews
However, this isn't particularly scalable. Consider implementing a more generic /<php-file> to /<php-file>.php rewrite instead. (An exercise for the reader.)
I have made a .htaccess file to rewrite /username to /profile.php?=username
this is my htaccess file
Options All -Indexes
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([a-zA-Z0-9_-]+)$ /profile.php?username=$1 [QSA,L]
the issue is that the other files in the same level are skipped unless if i added their .php extention which is awful.
Can i prevent /username rewrite if that file exist and also do another rewrite in order to have URL without file extention ?
You can keep these 2 rules in the given order:
Options All -Indexes -MultiViews
RewriteEngine On
## To internally rewrite /dir/file to /dir/file.php
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule ^(.+?)/?$ $1.php [L]
## for user profile
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([\w-]+)/?$ profile.php?username=$1 [QSA,L]
It is important to turn off -MultiViews (content negotiation service) of Apache for this.
#MrWhite nothing for now, i just want to stop rewrite when file exist
The rules would work together. If you check that the target .php file exists before rewriting the request (on your extensionless URLs) - as you should be - then you don't need to apply the same filesystem check on your existing rule that rewrites the request to profile.php.
For example:
# Append ".php" if request file without extension and target file exists
RewriteCond %{DOCUMENT_ROOT}/$1.php -f
RewriteRule ^([^.]+[^/])$ $1.php [L]
# Rewrite user profiles (directory check is optional)
#RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([a-zA-Z0-9_-]+)$ profile.php?username=$1 [QSA,L]
I'm assuming your URLs do not contain dots (this naturally avoids having to make an exception for requests that end in a file extension). The regex ^([^.]+[^/])$ matches URLs that do not contain a dot and do not end in a slash (ie. directories end in a slash).
Filesystem checks are relatively expensive, so are best kept to a minimum (or avoided altogether if possible). In the rule that appends the ".php" extension, there is no need to check that the request does not map to a directory before checking that the request does map to a file when .php is appended, these checks are mutually inclusive. (But if a directory did exist then the file wouldn't be accessible - catch 22.)
Likewise, there is no need to check that the user-profile URL does not map to a file, unless you also have files that don't have a file extension (very unlikely and best avoided anyway if you do).
Even the directory check on the user-profile URL is debatable. This is only required if you need to be able to access subdirectories off the root directory directly.
With this limited set of rules, it doesn't really matter whether MutliViews is enabled or not. (Although best practice would dictate that MultiViews should be disabled here, to avoid future conflicts.) The effect of having MultiViews enabled will just mean the first rule that appends the .php file extension is bypassed (not required). But having MultiViews enabled essentially enables extensionless URLs on everything.
Consider restructuring your user-profile URLs
HOWEVER, there is a fundamental "problem" with your user-profile URL structure - namely that they do "conflict" with actual file requests. The actual file requests will naturally take priority, but this means that you can't have usernames that happen to match files in the root directory - since the user profile will not be accessible. This check would need to be enforced when creating/updating user accounts.
It would be better to avoid this ambiguity to begin with and allow all usernames (that could also match root files) by creating a "unique" URL. eg. /user/<username>. This also completely avoids having to perform the directory check. For example:
# Rewrite user profiles (directory check is not required)
RewriteRule ^user/([a-zA-Z0-9_-]+)$ profile.php?username=$1 [QSA,L]
sorry, but i'am less understand about url rewrite...
i want to rewrite my url from :
http://localhost/controller/index.php/user/edit
to
http://localhost/controller/user/edit
i can make it with this .htaccess :
RewriteEngine On
RewriteBase /controller/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php/$1 [PT,L]
but, the rewrite works if there is no file exist at controller/user/edit.php
i want every request to under my controller/xxx is rewrited to controller/index.php/xxx whether the file is exist or not....
i have remove the RewriteCond so my current one is like this :
RewriteEngine On
RewriteBase /controller/
RewriteRule ^(.*)$ index.php/$1 [PT,L]
but, it shown internal service error..
There are a lot of things that don't make sense to me. Mainly, your question says to want to rewrite a URL having index.php in it to one that does not, but your rewrite rule, which you say works in some cases does the opposite, it pre-pends index.php to requests.
If you have access to your apache error and access log, you might see if there's more information about exactly at what point the error occurred -- was it when the .htaccess file was processed, or was it from within your php program?
I will assume that the goal here is to take "pretty" urls like /controller/user/edit and have the index.php program actually process the /user/edit part of the path.
If so, I think you may want to set the RewriteBase to /, and change your .htaccess to
RewriteEngine On
RewriteBase /
RewriteRule ^(.*)$ controller/index.php/$1 [PT,L]
The RewriteBase / directive says that all requests are relative to the server's DOCUMENT_ROOT setting. The change to the rewrite rule instructs all requests to go to the directory controller and file index.php, appending the original requested path afterwards.
(Note: I don't think you want to use the PT flag in this case, and it would be better form to escape the . which is a regex operator as index\.php, but I think neither of these are relevant to the problem here)
It is not clear if you do want the / before the $1. If your PHP program (index.php) is getting called with it present, and knows how to handle it, then it's fine, but it's a little unusual, and there may be cases where you end up with multiple /'s from within the php program.
But do you really want to do this? The typical use of the RewriteCond %{REQUEST_FILENAME} !-f is to handle cases such as image files and css or javascript files that are static and need not be handled by your controller. RewriteCond %{REQUEST_FILENAME} !-d depends on your system (but it's purpose to see that the request is not for a directory).
Anyway, the basic change as I proposed might help, but if not, perhaps you can clarify your intent and provide some actual URLs and a look inside index.php
I need to have URLs such as mydomain.com/whatever, where "whatever" can be any arbitrary string, all call the same php file where it sorts out what to display (or displays a 404). However, I want files and other php files to work normally (anything that is otherwise aliased, or that actually exists in the file system).
A simple AliasMatch /* myphpfile.php (after all the other Aliases in httpd.conf) works fine on my own setup, but on a production server, the wildcard alias sends all the other php files to myphpfile.php. I'm not sure what else might be confusing things.
Technically the whatever string will be alphabetic and lower case, so it can filter for that, but all attempts I've made with regex's haven't been successful.
Use these rules (you need mod_rewrite):
Options +FollowSymLinks -MultiViews
RewriteEngine On
RewriteBase /
# do not do anything for already existing files
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule .+ - [L]
RewriteRule ([a-z]+) /myfile.php [L]
Place in .htaccess in website root folder. If placed elsewhere some tweaking may be required.
This will rewrite (internal redirect) all NON-EXISTING single-lowercase-word requests to /myfile.php, where using $_SERVER['REQUEST_URI'] script can determine which URL was called and decide what to do (routing).
This will work for URLs like /whatever, but will do nothing for /what-ever, /hello/pinky, /hello123.
I want to be able to rewrite this
http://localhost/.../identicon/f528764d624db129b32c21fbca0cb8d6.png
to
http://localhost/.../identicon.php?hash=f528764d624db129b32c21fbca0cb8d6
so I add to the /.../.htaccess so this is it:
RewriteEngine On
RewriteRule ^resource/ - [L]
RewriteRule ^identicon/(.+)\.png$ identicon.php?hash=$1 [QSA,L]
RewriteRule ^(.*)$ index.php?t=$1 [QSA,L]
Which doesn't work for some reason because it redirects it to index.php?t=identicon.php; even though the L flag is set! Why?
Add a condition to the last rule to exclude requests that can be mapped to existing files:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php?t=$1 [QSA,L]
That is necessary because the L flag generates an internal redirect with the new URL as the request URL:
Remember, however, that if the RewriteRule generates an internal redirect (which frequently occurs when rewriting in a per-directory context), this will reinject the request and will cause processing to be repeated starting from the first RewriteRule.
(Not correct answer; left for reference)
I just figured out what may be the issue - it's something that thwarted me for a long time.
Depending on your server settings, it very well may be interpreting identicon/xxx.png as a request to identicon.php/xxx.png, assuming that the PHP extension is what you wanted. Try going to /index instead of /index.php - if it loads the PHP file, this is the issue affecting you.
This is the MultiViews Apache option, and it's stupid, but it has to be enabled specifically. Go into your site configuration file and see where it is enabled, and remove it.
If you don't have total control over your server configuration, the following may work in .htaccess (depending, ironically, on your server configuration).
Options -Multiviews