mod_rewrite omit html extension loop - apache

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

Rewrite URLs in .htaccess for replacing Query parameters with forward slash (id?=value)

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.

Apache: How can I both redirect foo.html to foo and rewrite foo to foo.html?

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]

Is there a proper order for processing 301 redirects and URL re-writes?

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]

.htaccess rewrite cond and rule cannot firgure out extra slash at end

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]

force remove .php extension in .htaccess

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]