How can a write correct rewrite rules in htaccess? - .htaccess

I havve been using an htaccess file to create rewrites so as not to not have to include .php/.html filenames in my urls.
RewriteEngine on
RewriteCond %{HTTP_HOST} ^example-page.com [NC,OR]
RewriteCond %{HTTP_HOST} ^www.example-page.com
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule /front /front_page.php
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule /redirect /redirect-page.php
However as my code is currently written the redirects are not working and all attempts at loading the pages with anything else besides the filenames is giving me a 404 error. How might i fix my code to get the redirecting to work?

Write the directives like this instead:
RewriteEngine on
RewriteRule ^front$ /front_page.php [L]
RewriteRule ^redirect$ /redirect-page.php [L]
There doesn't seem to be a need for all your additional conditions? I assume you only have the one domain example-page.com and you don't have a file called /front etc.
In .htaccess, the URL-path matched by the RewriteRule pattern does not start with a slash. You should also include start/end-of-string anchors on the regex so that you only match front exactly and not match it anywhere in the URL-path.

Related

htaccess dynamic url redirect

I have the following URL
https://example.com/expert-profile?id=john-doe&locale=en
I want it to be redirected to
https://example.com/expert/john-doe
I tried the following
RewriteCond %{QUERY_STRING} ^(([^&]*&)*)id=([^&]+)&?(.*)?$
RewriteRule ^expert-profile$ https://example.com/expert/%3?%1%4 [L,R=301]
And a couple of other solutions, nothing is working here. Can someone help me to go in the right direction?
Update:
This is my current .htaccess file
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule . /index.html [L]
</IfModule>
Redirect 301 "/en/download-app" "/download-app"
Please keep your htaccess file in your root and have it in following way.
Please clear your browser cache before testing your URLs.
RewriteEngine ON
RewriteCond %{QUERY_STRING} ^id=([^&]*)&locale=(.*)$ [NC]
RewriteRule ^([^-]*)-.*/?$ $1/%1-%2 [R=301,L]
OR in case you don't have Rules to handle non-existing files/directories then use following Rules set. Please make sure either use above OR following Rules set one at a time only.
RewriteEngine ON
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{QUERY_STRING} ^id=([^&]*)&locale=(.*)$ [NC]
RewriteRule ^([^-]*)-.*/?$ $1/%1-%2 [R=301,L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(?:expert)/([^-]*)-(.*)$ $1-profile?id=$1&locale=$2 [NC,L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule ^ /index.html [L]
I have following URL
https://example.com/expert-profile?id=john-doe&locale=en
I want it to be redirected to
https://example.com/expert/john-doe
You would need to do something like the following at the top of your .htaccess file, before your existing directives (order is important):
RewriteCond %{QUERY_STRING} (?:^|&)id=([^&]+)
RewriteRule ^expert-profile$ /expert/%1 [QSD,R=301,L]
This captures the value of the id URL parameter (in the %1 backreference) regardless of where it appears in the query string and discards all other URL parameters. I'm assuming you don't specifically need to match locale=en?
Note that the regex subpattern ([^&]+) (the id value) only matches something, not nothing. If the URL parameter is empty (ie. id=&locale=en) then no redirect occurs.
The QSD flag is necessary to discard the original query string.
Test first with a 302 (temporary) redirect to avoid potential caching issues. And clear your browser cache before testing. Only use a 301 (permanent) redirect if this really is intended to be permanent.
To redirect the specific URL /expert-profile?id=<name>&locale=en to /expert/<name>, ie. the id parameter is at the start of the query string and is followed by locale=en only then you can (and should) be more specific in the condition. For example:
RewriteCond %{QUERY_STRING} ^id=([^&]+)&locale=en$
RewriteRule ^expert-profile$ /expert/%1 [QSD,R=301,L]
RewriteCond %{QUERY_STRING} ^(([^&]*&)*)id=([^&]+)&?(.*)?$
RewriteRule ^expert-profile$ https://example.com/expert/%3?%1%4 [L,R=301]
This is close (providing you placed the rule at the top of the file), however, this tries to preserve the other URL parameters, ie. locale=en and whatever else, to create another query string - which you've not stated in your requirements.
Aside: The existing answers are assuming you are wanting to internally rewrite (URL rewrite) the request in the other direction, ie. from /expert/john-doe to /expert-profile?id=john-doe&locale=en. This is probably due to how questions of this nature are notoriously miswritten and this is often the real underlying intention. However, you've made no mention of this here and a URL of the form /expert-profile is not a valid endpoint - so it wouldn't really make sense to "rewrite" the URL in that direction. (?)
If you want it rewritten, capture the name (.+) and insert it into the target $1
RewriteRule ^expert/(.+)$ /expert-profile?id=$1&locale=en [L]
And don't use flag R|redirect here, unless you really want a redirect.---
To redirect from expert-profile?id=john-doe to expert/john-doe, capture the id (.+?) from the query string and insert it in the substitution URL %1
RewriteCond &%{QUERY_STRING}& &id=(.+?)&
RewriteRule ^expert-profile$ /expert/%1 [R,L]
When everything works as it should, you may replace R with R=301 (permanent redirect).
Don't use both rules together. If you do, it will result in an endless redirect loop and finally give a "500 Internal Server Error".
Unrelated, but never test with R=301!

mod rewrite to remove file extension, add trailing slash, remove www and redirect to 404 if no file/directory is available

I would like to create rewrite rules in my .htaccess file to do the following:
When accessed via domain.com/abc.php: remove the file extension, append a trailing slash and load the abc.php file. url should look like this after rewrite: domain.com/abc/
When accessed via domain.com/abc/: leave the url as is and load abc.php
When accessed via domain.com/abc: append trailing slash and load abc.php. url should look like this after rewrite: domain.com/abc/
Remove www
Redirect to 404 page (404.php) when accessed url doesn't resolve to folder or file, e.g. when accessing either domain.com/nothingthere.php or domain.com/nothingthere/ or domain.com/nothingthere
Make some permanent 301 redirects from old urls to new ones (e.g. domain.com/abc.html to domain.com/abc/)
All php files sit in the document root directory, but if there is a solution that would make urls such as domain.com/abc/def/ (would load domain.com/abc/def.php) also work it would be great as well, but not necessary
So here is what I have at the moment (thrown together from various sources and samples from around the web
<IfModule mod_rewrite.c>
RewriteCond %{HTTPS} !=on
# redirect from www to non-www
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^ http://%1%{REQUEST_URI} [R=301,L]
# remove php file extension
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{THE_REQUEST} ^GET\ /[^?\s]+\.php
RewriteRule (.*)\.php$ /$1/ [L,R=301]
# add trailing slash
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^.*[^/]$ /$0/ [L,R=301]
# resolve urls to matching php files
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule (.*)/$ $1.php [L]
With this the first four requirements seem to work, whether I enter domain.com/abc.php, domain.com/abc/ or domain.com/abc, the final url always ends up being domain.com/abc/ and domain.com/abc.php is loaded.
When I enter a url that resolves to a file that doesn't exists I'm getting an error 310 (redirect loop), when really a 404 page should be loaded. Additionally I haven't tried if subfolders work, but as I said, that's low priority. I'm pretty sure I can just slap the permanent 301 redirects for legacy urls on top of that without any issues as well, just wanted to mention it. So the real issue is really the non working 404 page.
I've had problems with getting ErrorDocument to work reliably with rewrite errors, so I tend to prefer to handle invalid pages correctly in my rewrite cascade. I've tried to cover a fully range of test vectors with this. Didn't find any gaps.
Some general points:
You need to use the DOCUMENT_ROOT environment variable in this. Unfortunately if you use a shared hosting service then this isn't set up correctly during rewrite execution, so hosting providers set up a shadow variable to do the same job. Mine uses DOCUMENT_ROOT_REAL, but I've also come across PHP_DOCUMENT_ROOT. Do a phpinfo to find out what to use for your service.
There's a debug info rule that you can trim as long as you replace DOCROOT appropriately
You can't always use %{REQUEST_FILENAME} where you'd expect to. This is because if the URI maps to DOCROOT/somePathThatExists/name/theRest then the %{REQUEST_FILENAME} is set to DOCROOT/somePathThatExists/name rather than the full pattern equivalent to the rule match string.
This is "Per Directory" so no leading slashes and we need to realise that the rewrite engine will loop on the .htaccess file until a no-match stop occurs.
This processes all valid combinations and at the very end redirects to the 404.php which I assume sets the 404 Status as well as displaying the error page.
It will currently decode someValidScript.php/otherRubbish in the SEO fashion, but extra logic can pick this one up as well.
So here is the .htaccess fragment:
Options -Indexes -MultiViews
AcceptPathInfo Off
RewriteEngine On
RewriteBase /
## Looping stop. Not needed in Apache 2.3 as this introduces the [END] flag
RewriteCond %{ENV:REDIRECT_END} =1
RewriteRule ^ - [L,NS]
## 302 redirections ##
RewriteRule ^ - [E=DOCROOT:%{ENV:DOCUMENT_ROOT_REAL},E=URI:%{REQUEST_URI},E=REQFN:%{REQUEST_FILENAME},E=FILENAME:%{SCRIPT_FILENAME}]
# redirect from HTTP://www to non-www
RewriteCond %{HTTPS} !=on
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^ http://%1%{REQUEST_URI} [R=301,L]
# remove php file extension on GETs (no point in /[^?\s]+\.php as rule pattern requires this)
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_METHOD} =GET
RewriteRule (.*)\.php$ $1/ [L,R=301]
# add trailing slash
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^.*[^/]$ $0/ [L,R=301]
# terminate if file exists. Note this match may be after internal redirect.
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^ - [L,E=END:1]
# terminate if directory index.php exists. Note this match may be after internal redirect.
RewriteCond %{REQUEST_FILENAME} -d
RewriteCond %{ENV:DOCROOT}/$1/index.php -f
RewriteRule ^(.*)(/?)$ $1/index.php [L,NS,E=END:1]
# resolve urls to matching php files
RewriteCond %{ENV:DOCROOT}/$1.php -f
RewriteRule ^(.*?)/?$ $1.php [L,NS,E=END:1]
# Anything else redirect to the 404 script. This one does have the leading /
RewriteRule ^ /404.php [L,NS,E=END:1]
Enjoy :-)
You'll probably want to check if the php file exists before adding the tailing slash.
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule ^.*[^/]$ /$0/ [L,R=301]
or if you really want a tailing slash for all 404 pages (so /image/error.jpg will become /images/error.jpg/, which I think is weird):
RewriteCond %{ENV:REDIRECT_STATUS} !200
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^.*[^/]$ /$0/ [L,R=301]
I came up with this:
DirectorySlash Off
RewriteEngine on
Options +FollowSymlinks
ErrorDocument 404 /404.php
#if it's www
# redirect to non-www.
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^ http://%1%{REQUEST_URI} [L,R=301,QSA]
#else if it has slash at the end, and it's not a directory
# serve the appropriate php
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)/$ /$1.php [L,QSA]
#else if it's an existing file, and it's not php or html
# serve the content without rewrite
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{REQUEST_FILENAME} -f
RewriteCond %{REQUEST_URI} !(\.php)|(\.html?)$
RewriteRule ^ - [L,QSA]
#else
# strip php/html extension, force slash
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^(.*?)((\.php)|(\.html?))?/?$ /$1/ [L,NC,R=301,QSA]
Certainly not very elegant (env:redirect_status is quite a hack), but it passes my modest tests. Unfortunately I can't test the www redirection, as I'm on localhost, and has no real access to a server, but that part should work too.
You see, I used the ErrorDocument directive to specify the error page, and used the DirectorySlash Off request to make sure Apache doesn't interfere with the slash-appending fun. I also used the QSA (Query String Append) flag that, well, appends the query string to the request so that it's not lost. It looks kind of silly after the trailing slash, but anyhow.
Otherwise it's pretty straightforward, and I think the comments explain it pretty well. Let me know if you run into any trouble with it.
Create a folder under the root of the domain
Place a .htaccess in the above folder as RewriteRule ^$ index.php
Parse the URL
With PHP coding you can now strip the URL or file extension as required

Trying to add trailing slash with htaccess, results in a absolute path

What I'm trying to achive is to have all urls on my page look like http://domain.com/page/, no extensions, but a trailing slash. If a user happends to write http://domain.com/page or http://domain.com/page.php it will redirect to the first url. After some googling i found this code, and it's close to working, but when you leave out the trailing slash in your request the url becomes something like http://domain.com/Users/"..."/page/ and therefor returns a 404.
My .htaccess looks like this:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{THE_REQUEST} ^GET\ /[^?\s]+\.php
RewriteRule (.*)\.php$ /$1/ [L,R=301]
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule (.*)/$ $1.php [L]
RewriteCond %{REQUEST_FILENAME} !(.*)/$
RewriteRule (.*)/$ $1.php [L]
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule .*[^/]$ $0/ [L,R=301]
I've been trying to add an additional rule but I really don't get any of this and I haven't been able to find any answers.
For a scenario like this one, the .htaccess author has to consider both what the browser URL bar should display and what file the web server should return/execute. Note also that each external redirect starts the processing of the rewrite directives over.
With that in mind, start by taking care of which file is returned when the URL is in the correct format:
RewriteEngine on
RewriteRule ^/?$ /index.php [L]
RewriteRule ([^./]+)/$ /$1.php [L]
Then, deal with URLs with no trailing slash by redirecting them with [R=301]:
RewriteRule ^/(.*)\.[^.]*$ http://www.example.com/$1/ [R=301,L]
RewriteRule ^/(.*)$ http://www.example.com/$1/ [R=301,L]
Note that the first of these two rules should also take care of the case where there is a filename (like something.php) but also a trailing slash by eliminating the filename extension and re-adding the slash.
Keep in mind that, if your internal directory structure does not match what the web server is serving (as is often the case in shared hosting scenarios), you will likely need to add a RewriteBase directive immediately after the RewriteEngine directive. See the Apache docs for an explanation.

Rewrite rule to hide folder, doesn't work right without trailing slash

i have a strange apache mod_rewrite problem. I need to hide a sub-directory from the user, but redirect every request to that sub-directory. I found several quite similar issues on stackoverflow, but nothing really fits, so i decided to post a new question.
My .htaccess looks like this:
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule ^(.*)?$ foo/$1 [QSA,L]
The document-root only contains the following folder/files:
/foo/bar/index.html
I would now expect that example.com/bar and example.com/bar/ would just show me the contents of index.html.
Instead example.com/bar/ show me the content as expected but example.com/bar redirects me with a 301 to example.com/bar/foo/ an then shows the contents. I really don't get why there is a 301 redirect in this case.
When i put something this
RewriteCond %{REQUEST_URI} !^[^.]*/$
RewriteCond %{REQUEST_URI} !^[^.]*\.html$
RewriteCond %{REQUEST_URI} !^[^.]*\.php$
RewriteRule ^(.*)$ $1/ [QSA,L]
on top of that rule it seems to work, but that would require me to list every used file extension...
Is there any other way i can omit the redirect, the folder "bar" should never be seen by an outside user.
Thanks in advance!
1st rewrite rule is redirect from /foo/(.) to ($1) and second - from (.) to $1.
just idea, this has not been tested.
Better late than never...
Got it working with a simple RewriteRule which append a / to every url that doesn't have on.
# only directories
RewriteCond %{REQUEST_FILENAME} !-f
# exclude there directories
RewriteCond %{REQUEST_URI} !^/excluded-dirs
# exclude these extensions
RewriteCond %{REQUEST_URI} !\.excluded-extension$
# exclude request that already have a /
RewriteCond %{REQUEST_URI} !(.*)/$
RewriteRule ^(.*)$ /$1/ [R=301,L]

htaccess directory to file redirect problem

I’m trying to use the following .htaccess file
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-l
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !^images/
RewriteRule (.*) view.php?picid=$1 [L]
RewriteRule ^/user/(.*)$ /users.php?user=$1
I want two things to happen: Whenever someone requests /1234, it redirects to /view.php?picid=1234, and also when someone visits /users/bob, it redirects to /users.php?user=bob.
My code however, doesn’t seem to be working correctly.
There are several ways to do that. Here’s one that should work:
RewriteRule ^user/(.+)$ users.php?user=$1 [L]
RewriteRule ^([0-9]+)$ view.php?picid=$1 [L]
The first rule will catch any request that’s URI path begins with /user/ followed by one or more arbitrary characters. And the second will catch any request that’s URI path begins with / followed by one or more digits.
The initial problem with your rules is that the RewriteRule with (.*) will match everything.
If you do not want it to match a URL with a slash in it (such as users/bob), try ^([^/]*)$
Secondly, after a URL is rewritten, the new URL goes through your rules again. If you want to avoid matching something that has already been rewritten once, you should add a condition like
RewriteCond %{REQUEST_URI} !\.php

Resources