Htaccess conditional https for static pages - php

My site is HTTPS enabled and all the pages are served using HTTPS only. Client now has the requirement where he wants to show static pages like about-us, termsofus as HTTP pages and not as HTTPS. This means that even if the user tries to open about-us page as HTTPS it should redirect to HTTP version of about-us.
My .htaccess code is as follows:
Options -Indexes
<IfModule mod_rewrite.c>
RewriteEngine on
#RewriteCond %{HTTPS} off
#RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
RewriteCond %{HTTPS} off
RewriteCond %{REQUEST_URI} !^\/about-us
RewriteCond %{REQUEST_URI} !^\/termsofus
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
RewriteCond %{HTTPS} on
RewriteCond %{REQUEST_URI} \/about-us [OR]
RewriteCond %{REQUEST_URI} \/termsofus
RewriteRule (.*) http://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . index.php
</IfModule>
Problem: Whenever I open HTTP/HTTPS version of about-us page it keeps on redirecting me to index.php.
For example: https://example.com/about-us to https://example.com/index.php
The site uses PHP YII framework.

Use THE_REQUEST variable instead of REQUEST_URI. THE_REQUEST variable represents original request received by Apache from your browser and it doesn't get overwritten after execution of some rewrite rules.
Options -Indexes
RewriteEngine on
RewriteCond %{HTTPS} off
RewriteCond %{THE_REQUEST} !\s/+(about-us|termsofus) [NC]
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301,NE]
RewriteCond %{HTTPS} on
RewriteCond %{THE_REQUEST} \s/+(about-us|termsofus) [NC]
RewriteRule ^ http://%{HTTP_HOST}%{REQUEST_URI} [L,R=301,NE]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . index.php
Make sure to clear your browser cache before testing this change.

anubhava's answer already addresses the problem and provides a good solution. I thought I'd just provide some additional explanation as to what was happening with your stated example with reference to your original code:
For example: https://example.com/about-us to https://example.com/index.php
Given a request for https://example.com/about-us:
This matches your second rule (HTTPS is "on" and about-us is requested) and redirects to http://example.com/about-us (ie. back to HTTP).
The redirected request (ie. http://example.com/about-us) now matches the last rule and gets internally rewritten to index.php (the front-controller).
However, in per-directory .htaccess files (directory context) "the rewritten request is handed back to the URL parsing engine" and the process effectively starts over. The REQUEST_URI server variable is also updated to hold the rewritten URL ie. /index.php.
On the second pass through the .htaccess file the request (now rewritten to http://example.com/index.php) matches your first rule (HTTPS is "off" and the request is not /about-us or /termsofus) so the request is redirected (a second time) to https://example.com/index.php. (The internal rewrite is effectively changed into an external redirect.)
The redirected request (now https://example.com/index.php) does not match any rules in your .htaccess file, so passes through unchanged. Page is served.
If you check the network traffic, you should see the two external redirects mentioned above.
Another possible solution is to use the END flag (Apache 2.4+ only) on the last RewriteRule. This effectively ends the URL rewriting process, so the process stops at step #2. Although I would still favour anubhava's solution and check against THE_REQUEST instead, which works on Apache 2.2 and will still work should you introduce additional rewrites.

Related

How to redirect any input to both https and www?

I need example.com be redirected to https://www.example.com.
My .htaccess file is in the root directory and configured like this:
RewriteEngine On
DirectoryIndex html/home5.html
RewriteRule ^\.htaccess$ - [F]
RewriteCond %{HTTPS} !on
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
With this configuration, www.example.com, https://example.com and http://example.com are all redirected to https://www.example.com (www.example.com/other.html and so on work respectively) correctly.
But example.com gets redirected to https://www.example.com/http://www.example.com/ and obviously produces "The requested URL was not found on this server".
Why does this happen? What do I need to change to make it work?
I also donĀ“t understand why with my current configuration https://example.com the www is added correctly.
Got a well working .htacces i'm using for the same problem, with 2 steps :
RewriteCond %{HTTPS} off
RewriteRule (.*) https://%{HTTP_HOST}/$1 [R=301,L]
First, if the requested url got HTTPS "off", redirect to HTTPS and add the url request query after (saved in $1).
RewriteCond %{HTTP_HOST} ^www\.(.*)$ [NC]
RewriteRule ^(.*)$ https://www.%1/$1 [L]
Then, special case for www. subdomain and https on, if matching "www" in the request, redirect to the same page with https on !
EDIT : you don't even need the second condition in fact
Redirect to https and non-www
To instead redirect all requests to https and non-www, use the following code instead of the previous:
Canonical HTTPS/non-WWW
<IfModule mod_rewrite.c>
RewriteCond %{HTTPS} off [OR]
RewriteCond %{HTTP_HOST} ^www\.example\.com [NC]
RewriteRule (.*) https://example.com/$1 [L,R=301]
</IfModule>
As before, place this code in the root .htaccess of your site. Here is what it's doing:
Checks if mod_rewrite is available
Checks if HTTPS is off, or if the request includes www
If either condition matches, the request qualifies and is redirected to the https/non-www address
When placed in the root .htaccess, this technique covers all requests, providing complete https/non-www canonicalization for your site. Remember to replace the two instances of example.com with your own domain name.
Note: if your site is suffering from duplicate pages because of index.php appended to requested URLs, check out this post at WP-Mix.com that explains how to remove www and index.php from the URL.
Redirect to https and www
The following .htaccess technique redirects qualified requests to the https and www versions of your web pages. Add to your site's root .htaccess file:
Canonical https/www
<IfModule mod_rewrite.c>
RewriteCond %{HTTPS} off
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteRule (.*) https://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
</IfModule>
This code does the following:
The first two lines conditionally redirect to https. If the HTTPS variable is set to off, then the request is redirected to https (see notes below if using a proxy).
The second two lines redirect to www. If the request/host does not begin with www., the request is redirected to www.
When placed in the root .htaccess, this technique covers all requests, providing complete https/www canonicalization for your site. No editing is required with this code; it's entirely plug-n-play.
Notes if using a proxy
As explained here:
"When behind some forms of proxying, whereby the client is connecting via HTTPS to a proxy, load balancer, Passenger application, etc., the %{HTTPS} variable may never be on and cause a rewrite loop. This is because your application is actually receiving plain HTTP traffic even though the client and the proxy/load balancer are using HTTPS. In these cases, check the X-Forwarded-Proto header instead of the %{HTTPS} variable."
So if you are using some sort of proxy service or similar, add the following line to the above code:
RewriteCond %{HTTP:X-Forwarded-Proto} !https
So the final result looks like this if using a proxy server:
Canonical https/www (when using proxy)
<IfModule mod_rewrite.c>
RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteCond %{HTTPS} off
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteRule (.*) https://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
</IfModule>
reference

How to avoid the redirection to index.php when trying to access non www urls

I have a Laravel website in production and here is my problem explained with 4 simple url tests:
Access: https://www.my-website.kr/
Result: https://www.my-website.kr/ (all good)
Access: https://my-website.kr/ (home page again but without www
Result: https://www.my-website.kr/ (all good, redirects my non www
urls to www)
Access: https://www.my-website.kr/subpage
Result: https://www.my-website.kr/subpage (all good)
Access: https://my-website.kr/subpage
Result: https://www.my-website.kr/index.php (not good, I don't want this index.php)
This last test it the one I cannot fix and it is quite annoying. When I access a subpage without www; it's okay if the only solution is to get a redirection to the home page again but at least without the index.php this is terrible for the SEO.
I know these questions about htaccess have been answered many times but I am loosing hope... Even the technical support of my dedicated server couldn't answer me properly.
I have two htaccess files at the moment; one located directly at the root of my public_html/ with the following content:
RewriteEngine On
RewriteCond %{HTTP_HOST} !^$
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteCond %{HTTPS}s ^on(s)|
RewriteRule ^ http%1://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
RewriteCond %{HTTP_HOST} ^domain\.kr [NC]
RewriteRule ^(.*)$ https://www.domain.co.kr/$1 [L,R=301,NC]
RewriteCond %{HTTP_HOST} ^domain\.co\.kr [NC]
RewriteRule ^(.*)$ https://www.domain.co.kr/$1 [L,R=301,NC]
RewriteCond %{HTTPS} !on
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}
And another one under public_html/public with the default Laravel's htaccess content:
<IfModule mod_rewrite.c>
<IfModule mod_negotiation.c>
Options -MultiViews
</IfModule>
RewriteEngine On
# Redirect Trailing Slashes If Not A Folder...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)/$ /$1 [L,R=301]
# Handle Front Controller...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
# Handle Authorization Header
RewriteCond %{HTTP:Authorization} .
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
</IfModule>
You might ask me why have I two htaccess at two different location. And I wonder the same. The thing is I have done many test and I discovered that putting the www / https rules directly at the root folder kind of worked better. But I might be wrong.
Again sorry if this question has been asked a million time but I couldn't find the answer that would work for me.
Thanks in advance for any bit of help.
The .htaccess file taking care of all the redirects (www or not, https or not) is over-engineered.
It seems like the final domain should always be www.domain.co.kr + SSL so there's no need to use %{HTTP_HOST} as the final domain is not dynamic, or that %{HTTPS}s ^on(s) match to extract s or not.
The order is correct tho: always do all the nitty gritty redirects/http(s) before the framework rewrites.
Because you do that in the parent folder, it's OK. You could put those rules in the same .htaccess file too, but you'd have to put them before the Laravel ones.
I would start with simplifying it:
RewriteEngine On
# Redirect http to https
RewriteCond %{HTTPS} !on
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L,QSA]
# Redirect non-www to www
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteRule (.*) https://www.domain.co.kr/$1 [R=301,L,QSA]
You'll notice I also added the QSA flag to pass any query string you might have in the original request.
See how you go from there.
Also make sure you always empty your browser cache every time you try something new as redirects are cached.
If it's still not working and you can change the log level on your apache config, check this: http://httpd.apache.org/docs/current/mod/mod_rewrite.html#logging
You'll be able to debug what happens during redirects/rewrites, step by step, to pin point where the actual issue is.

All my .htaccess redirects are going to index.php

I am trying to redirect all my website traffic from any url to https://, secure ssl using a .htaccess file. This has to match the current domain and redirect including any sub url's.
My code:
RewriteCond %{HTTPS} off
RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteCond %{HTTP_HOST} ^shop.test.com$
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
The issue i am having is that all traffic is being sent to index.php
shop.test.com/testurl
goes to - https://shop.test.com/index.php
expected - https://shop.test.com/testurl
This is most likely due to another rule that writes everything to index.php and uses that as front controller.
Make sure you place above rule as your very first rule just below RewriteEngine On line.

Zend Framework /index/ redirect throught htaccess to avoid content duplication

I'd like to know if it is possible to add a rule to the htaccess of my ZF app to redirect all the URLs that ends with the segment /index/ (such as http://domain.ext/index/) to the same URL without the /index/ suffix.
I've tried with this simple rule:
RedirectMatch ^(.*)/(index(/)?)$ http://localhost$1
but it doesn't work as expected (with other frameworks such as FuelPHP it works like a charm).
I know that this can be done via PHP using a plugin but I'd like to make the redirect via Apache to improve the performance of the application.
I don't know why nobody jumped in here, it is not that complicated?
A config file is executed from top to bottom and certain rules cause an immediate exit. If the rule defines an external redirect the server will perform that redirect immediately and all following rules are therefore ignored. If the redirect is back to the same server and config file then it is just a new game with the rules! If the redirect rule does not apply anymore it is on to the next rule. If the rule would still apply you get a loop.
Similar thing with a RewriteRule that matches and has [L]. L means "Stop the rewriting process here and don't apply any more rewrite rules". This quote is straight from the manual
Now you simply have to define some logic in what order you want to apply certain rules. Your request about the RedirectMatch for any /index/ path is certainly something you want to have very early to the top of the config. If there is a match your config will end here and perform a redirect! The browser will send a new request and we have a new game.
The RewriteRule to an index.php is something we will add very late at the bottom. It may be our last resort like a if all fails then rule. I does not matter if this is the Zend Framework or any other application you funnel through an index.php or other script for that matter.
The following rules should cover any variation with index, including .php, .htm and .html and finally trigger the index.php file for your ZF application.
RedirectMatch ^(/.*)/(index.(php|html|htm)|index)/?$ $1
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteRule ^.*$ index.php [NC,L]
When testing redirect rules be careful with your browser and use one where you can totally reset all cache and history settings. All current browsers are notorious in "remembering" redirects. If they learned a redirect rule they will perform that redirect internal, i.e. they don't go to the server to see what's new!
Here is your ruleset laid out readably
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RedirectMatch ^(.*)/(index(/)?)$ http://localhost$1
RewriteRule ^.*$ - [NC,L]
RewriteRule ^.*$ index.php [NC,L]
RedirectMatch is a mod_alias directive which severs the conds as from their rule. Also it's a lot less fraught not mixing mod_alias and mod_rewrite directives, so try:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
RewriteRule ^(.*?)/index/?$ $1 [R=301,L]
RewriteRule ^.*$ index.php [NC,L]
(updated following posters comments)
More footnotes
I tried this out on my VM which mirrors my hosting service, but having root access I can see the 'production' rewrite logs. This fails because the second rules still falls through to rule (3) which dispatchs to index.php. This then returms the full content but with a 301 status and without issuing a new location. If I change the [R=301] to [R=301,L] then it works fine as the server now issues a 301 with a Location header and the browser now retries with the new location.
The documentation states:
You will almost always want to use [R] in conjunction with [L] (that is, use [R,L]) because on its own, the [R] flag prepends http://thishost[:thisport] to the URI, but then passes this on to the next rule in the ruleset, which can often result in 'Invalid URI in request' warnings.
I resolved my problem with this (horrible) workaround:
- I renamed the "index" action of IndexController to "home"
- I setup a static route for home page (source)
- I changed the htaccess to:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteRule ^(.*)home(/)?$ $1 [R=301,L]
RewriteRule ^(.*)index/(.*)$ $1$2 [R=301,L]
RewriteRule ^.*$ index.php [QSA,NC,L]
So now the home page is not duplicated because http://localhost/home/ is redirected to the base domain and for other controllers the index action, if it is called specifying the action name (/controller/index/param/value) is redirected to the desired URL (/controller/param/value/)
RewriteRule ^(.*)/index(?|$)$ $1$2 [R=301,L]
this works with urls
/index => /
/index?page=2 => /?page2
/index/index => /
/index/index?page=2 /?page2
you need remove trailing slash before, for url like /index/, index/index/
RewriteRule ^(.*)/$ /$1 [L,R=301]
url like /index/help will work without changes

mod_rewrite to remove .php but still serve the .php file?

I just wanted to do a simple thing with mod_rewrite. I have a site which uses .php files, and I wanted to rewrite those to cleaner URLs, and remove the .php. So, files would be www.mysite.com/contact and so on.
This does work how I wanted, but I had expected that it would still serve my contact.php file, but just show the user that they were at /contact rather than contact.php. But, it is looking for a file just called contact, which, is not there.
So, what so I need to do, do still use my contact.php file, but rewrite the URL for the user to /contact ?
Here is what I am using:
SetEnv APPLICATION_ENV development
RewriteEngine on
RewriteBase /
# Always use www.
RewriteCond %{HTTP_HOST} ^mysite\.com$ [NC]
RewriteRule ^(.*)$ http://www.mysite.com/$1 [L,R=301]
# Change urlpath.php to urlpath
RewriteCond %{HTTP_HOST} ^www\.mysite\.com$ [NC]
RewriteRule ^(.*)\.php$ http://www.mysite.com/$1 [L,R=301]
For this solution, I have followed the following rules:
If the user tries to load /something.php they should be externally redirected to /something.
If the user tries to load /something then they should be internally redirected to /something.php.
If the user passed any query string parameters to the URL then these should be preserved through the redirects.
If the user tries to load a different file which really exists on the filesystem (a stylesheet, image etc) then this should be loaded as is.
And here's the final set of mod_rewrite magic:
RewriteEngine on
RewriteBase /
## Always use www.
RewriteCond %{HTTP_HOST} ^mysite\.com$ [NC]
RewriteRule ^(.*)$ http://www.mysite.com/$1 [L,R=301]
# Change urlpath.php to urlpath
## Only perform this rule if we're on the expected domain
RewriteCond %{HTTP_HOST} ^www\.mysite\.com$ [NC]
## Don't perform this rule if we've already been redirected internally
RewriteCond %{QUERY_STRING} !internal=1 [NC]
## Redirect the user externally to the non PHP URL
RewriteRule ^(.*)\.php$ $1 [L,R=301]
# if the user requests /something we need to serve the php version if it exists
## Only perform this rule if we're on the expected domain
RewriteCond %{HTTP_HOST} ^www\.mysite\.com$ [NC]
## Perform this rule only if a file with this name does not exist
RewriteCond %{REQUEST_FILENAME} !-f
## Perform this rule if the requested file doesn't end with '.php'
RewriteCond %{REQUEST_FILENAME} !\.php$ [NC]
## Only perform this rule if we're not requesting the index page
RewriteCond %{REQUEST_URI} !^/$
## Finally, rewrite the URL internally, passing through the user's query string
## using the [qsa] flag along with an 'internal=1' identifier so that our first
## RewriteRule knows we've already redirected once.
RewriteRule ^(.*)$ $1.php?internal=1 [L, QSA]
Your third rule should be the other way around:
# Change urlpath.php to urlpath
RewriteCond %{HTTP_HOST} ^www\.mysite\.com$ [NC]
RewriteCond %{REQUEST_FILENAME} !\.php$ [NC]
RewriteRule ^/?(.*)$ $1.php [L,R=301]
Once the user goes to /contact, it'll load contact.php. The extra RewriteCond is so that if people DO go to contact.php, it won't try to load contact.php.php
As I understand you want the URL to be /contact even if the URL was /contact.php.
You can check for the .php extension and do a redirect to remove it. Use R=301 (as you do).
Then you have to make your server accept the URL without the .php extension. It might actually already do that.
That's what mod_negotiation does. It should be installed by default, but you might have to enable it.
You can also do that with mod_rewrite, but remove the R from the options. It will redirect internally instead of answering with an HTTP redirect.

Categories