I have the following scenario:
Two frontcontrollers in a web directory (document root):
web/frontend.php # handles all *.html requests
web/backend.php # direct calls only
Rewriting is easy so far:
RewriteCond %{REQUEST_URI} !^/backend.php
RewriteRule (.+)\.html$ /frontend.php [L]
So now when I call example.org/backend.php I'm in the backend, nothing special happens. And when I call something like example.org/ or example.org/team/john.html it is handled by frontend.php.
Works so far!
Now I want the possibility to omit the *.html extension so that example.org/team/john is internally handled as example.org/team/john.html.
RewriteCond %{REQUEST_FILENAME}.html -f
RewriteRule !.*\.html$ %{REQUEST_URI}.html [L]
Last but not least I want to redirect requests to john.html to john to avoid duplicate content.
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} -f
RewriteCond %{REQUEST_URI} ^(.+)\.html$
RewriteRule (.*)\.html$ /$1 [R=301,L]
Every part works on it's own but put together I get a loop, which doesn't surprise me but I don't know how to avoid this. I searched the docs, tried several flags and conditions but I'm totally stuck and I need help.
Here is the whole .htaccess file:
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteBase /
# extend html extension internally
RewriteCond %{REQUEST_FILENAME}.html -f
RewriteRule !.*\.html$ %{REQUEST_URI}.html [L]
# redirect example.html to example
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} -f
RewriteCond %{REQUEST_URI} ^(.+)\.html$
RewriteRule (.*)\.html$ /$1 [R=301,L]
# frontcontroller
RewriteCond %{REQUEST_URI} !^/backend.php
RewriteRule (.+)\.html$ /frontend.php [L]
</IfModule>
Any help would be great.
To avoid a loop, you can use THE_REQUEST
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} -f
RewriteCond %{THE_REQUEST} "\.html "
RewriteRule ^(.*)\.html$ /$1 [R,L]
Unrelated, but you can simplify your rules. First one
RewriteCond %{REQUEST_URI} !^/backend.php
RewriteRule (.+)\.html$ /frontend.php [L]
You already check for (.+)\.html, so you can omit the RewriteCond. Next, you don't use the captured part (.+). Replace it with . to ensure it's not empty. This gives then
RewriteRule .\.html$ /frontend.php [L]
Second one, unless you have *.html.html files in your site, you don't need to check for !html and can just use ^ for the RewriteRule pattern
RewriteCond %{REQUEST_FILENAME}.html -f
RewriteRule ^ %{REQUEST_URI}.html [L]
The loop is because of the multiple internal redirections, You can use END flag to prevent the rewrite loop
RewriteRule ^(.+)\.html$ /$1 [L,R=301]
RewriteCond %{REQUEST_FILENAME}.html -f
RewriteRule !.*\.html$ %{REQUEST_URI}.html [END]
Related
I have made sure that rewrite engine is enabled and removing .php extensions is working so I know that isn't the issue.
what I'm trying to do is simply remove the ?id=value aspect of the URL, so basically making the URL look like such:
folder/medias/value
Instead of
folder/medias?id=value
My current .htaccess looks like this:
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteRule ^(.*)$ $1.php [NC,L]
RewriteRule ^404/?$ /404.php [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule ^ 404.php [L,R]
With your shown samples/attempts, please try following htaccess Rules. Please make sure to clear your browser cache before testing your URLs.
RewriteEngine ON
##Rules for external rewrite.
RewriteCond %{THE_REQUEST} \s/([^.]*)\.php\?id=(\S+)\s [NC]
RewriteRule ^ /%1/%2? [R=301,L]
##Rule for internal rewrite.
RewriteRule ^([^/]*)/([^/]*)/?$ $1?id=$3 [L]
You may try this code inside the /folder/.htaccess (create this file if it doesn't exist):
RewriteEngine On
# External redirect from /folder/media?id=val to /folder/media/val
RewriteCond %{THE_REQUEST} /(\S+?)\.php\?id=([^&\s]+)\s [NC]
RewriteRule ^ /folder/%1/%2? [R=301,L,NE]
# Internal rewrite from /folder/media/val to /folder/media?id=val
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([\w-]+)/([\w-]+)/?$ $1.php?id=$2 [L,QSA]
Trailing ? in first rule is to remove query string from original URL.
%{REQUEST_FILENAME} !-f and %{REQUEST_FILENAME} !-d is to skip existing files and directories from rewrite in 2nd rule.
I'm hosting a website that's just a bunch of static .html files. I don't want to have the .html file extension in the URLs, so I've added a rewrite rule:
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.html -f
RewriteRule ^(.+)$ /$1.html [L]
This is simple enough and works fine, but I would now also like to redirect (302) URLs ending in .html to the canonical path, i.e. without the file extension. I've tried the following:
RewriteCond %{REQUEST_FILENAME} \.html$
RewriteRule ^(.+)\.html$ /$1 [L,R]
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.html -f
RewriteRule ^(.+)$ /$1.html [L]
However, that leads to an endless redirect loop. I suspect that's because the second rule, the internal rewrite, is still triggering the first rule, the external redirect.
How else can I achieve this? I've looked through all the rewrite flags and tried a bunch that sounded promising, but I haven't managed to make this work. How can I both rewrite from foo.html to foo and still do an internal rewrite from foo to foo.html?
There are several questions asking about how this procedure works. Use THE_REQUEST variable:
RewriteCond %{THE_REQUEST} ^GET\ /(.+)\.html
RewriteRule \.html$ /%1 [R=302]
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}.html -f
RewriteRule ^(.+)$ /$1.html [L]
Okay, the situation is like this. I have example.com/old-url.php, as one example, which I eventually want to become example.com/new-url/.
Since I am doing this for multiple pages across the website, I first remove the PHP extension and force trailing slashes for all requests, like so:
RewriteCond %{THE_REQUEST} ^GET\ /[^?\s]+\.php
RewriteRule (.*)\.php$ /$1/ [L,R=301]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} ^/(.+)/$
RewriteCond %{DOCUMENT_ROOT}/%1.php -f
RewriteRule ^(.*)/$ $1.php [L]
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule .*[^/]$ $0/ [L,R=301]
For some pages, that is all that is required. For other pages, such as my example above, I will need to take one more step. So I rename old-url.php to new-url.php and create an additional 301 redirect, like so:
Redirect 301 /old-url/ http://www.example.com/new-url/
But is this the proper way to do it? Would it be better practice to do something like:
Redirect 301 old-url.php http://www.example.com/new-url.php
And simply place the 301's before the URL re-writes? Or even:
Redirect 301 old-url.php http://www.example.com/new-url/
It seems like there are several ways I could set this up, and I was just wondering if it really matters or which is optimal.
Thanks in advance.
One should keep external redirects before internal routing rules. Also not a good idea to mix mod_rewrite rules with mod_alias rules.
This is proper order:
RewriteRule ^old-url/?$ /new-url/ [L,NC,R=301]
RewriteCond %{THE_REQUEST} ^GET\ /[^?\s]+\.php
RewriteRule ^(.+?)\.php$ /$1/ [L,R=301]
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule .*[^/]$ $0/ [L,R=301]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} ^/(.+)/$
RewriteCond %{DOCUMENT_ROOT}/%1.php -f
RewriteRule ^(.*)/$ $1.php [L]
I've been googling around stack overflow trying to figure this out but have had no joy and keep getting errors no matter what I try.
i am currently using this code to rewrite the address to remove extensions, I got it from a SO post a while ago for simple websites:
RewriteEngine on
# To externally redirect /dir/foo.html to /dir/foo
RewriteCond %{THE_REQUEST} ^[A-Z]{3,}\s([^.]+)\.html [NC]
RewriteRule ^ %1 [R=301,L]
# To internally forward /dir/foo to /dir/foo.html
RewriteCond %{REQUEST_FILENAME}.html -f
RewriteRule ^ %{REQUEST_URI}.html [L]
# To externally redirect /dir/foo.html to /dir/foo
RewriteCond %{THE_REQUEST} ^[A-Z]{3,}\s([^.]+)\.phtml [NC]
RewriteRule ^ %1 [R=301,L]
# To internally forward /dir/foo to /dir/foo.html
RewriteCond %{REQUEST_FILENAME}.phtml -f
RewriteRule ^ %{REQUEST_URI}.phtml [L]
(i use phtml files as ui files and php for background files, hence the second block)
This works fine for me but now i am on a site where i would like to feed get variables to a page for bookmarking etc and do not want the address to be some thing like www.website.com/page?var=0
i have managed to get my testing site to load www.website.com/page/ using code from another SO post, but only if i enter that url, it will not rewrite if i go to page.html and will not load the attached files css, js (i understand this is because the files have a relative path) Edit: I now understand this is to do with -f || !-f;
code:
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.html -f
RewriteRule ^([^/]+)/$ $1.html
RewriteRule ^([^/]+)/([^/]+)/$ /$1/$2.html
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !(\.[a-zA-Z0-9]{1,5}|/)$
RewriteRule (.*)$ /$1/ [R=301,L]
I am fine with either having or not having a trailing slash, but I do want to add it when there are variables, like page?var=0 to page/var=0 without the images, js and css stopping. of course if it can all be with a slash then all the better.
Thanks
Edit: Also needs to work when the website and .htaccess are in a subfolder, like: mysite.com/example/websitedemonstration/page
This one seems to work fine for me, give it a try:
Options +FollowSymLinks -MultiViews
RewriteEngine On
RewriteBase /
# To externally redirect /dir/foo.html to /dir/foo
RewriteCond %{THE_REQUEST} ^[A-Z]{3,}\s([^.]+)\.(html|phtml) [NC]
RewriteRule ^ %1 [R=301,L]
# To internally forward /dir/foo to /dir/foo.html
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.html -f
RewriteRule ^([^/]*)/?$ $1.html [L]
# To internally forward /dir/foo to /dir/foo.phtml
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.phtml -f
RewriteRule ^([^/]*)/?$ $1.phtml [L]
i know there are many questions like "How do i remove the .php extension, so that /test/ will be /test.php"
but if the user directly goes to test.php it doesnt replace the extension.
So I want to replace the .php it should be /.
here is the part of the .htacces I'm using:
RewriteEngine on
RewriteRule stuff\.php /other_stuff/ [R=301,L]
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteRule ^([^/]+)/$ $1.php
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !(\.[a-zA-Z0-9]{1,5}|/)$
RewriteRule (.*)$ /$1/ [R=301,L]
You need to do the check against the actual request instead of the URI (which gets rewritten by other rules). The %{THE_REQUEST} variable doesn't change in the course of the rewrite engine. Try adding these rules right below the RewriteEngine directive:
RewriteCond %{THE_REQUEST} ^(GET|HEAD)\ /([^/]+)\.php(\?|\ |$)
RewriteRule ^ /%2/ [L,R=301]