RewriteRule + HTTPS redirect for specific requests - apache

I have setup the htaccess file to handle users requests with a specific rule and generic requests with other rules:
# users requests (i.e. users/login.html)
RewriteRule ^users/([^/]+)\.html$ mvc.php?rt=users/$1 [L,QSA,NC]
# generic requests (controller/action/id.html)
RewriteRule ^([^/]+)/([^/]+)/([^/]+)\.html$ mvc.php?rt=$1/$2&id=$3 [L,QSA,NC]
# generic requests (controller/action/)
RewriteRule ^([^/]+)/([^/]+)/$ mvc.php?rt=$1/$2 [L,QSA,NC]
# generic requests (controller/id.html)
RewriteRule ^([^/]+)/([^/]+)\.html$ mvc.php?rt=$1&id=$2 [L,QSA,NC]
# generic requests (controller.html)
RewriteRule ^([^/]+)\.html$ mvc.php?rt=$1 [L,QSA,NC]
RewriteRule ^$ mvc.php?rt=index [L,QSA,NC]
This works fine, and URL translation is handled correctly. My goal is now to force all users requests to be sent over HTTPS. I'm trying to setup a redirect rule as described in RewriteHTTPToHTTPS. I know this is not the recomended solution, but my server doesn't seem to support VirtualHost statements. Here is my code:
# users http to https
RewriteCond %{HTTPS} !=on
RewriteRule ^users(.*) https://%{SERVER_NAME}/users$1 [R,L]
Unfortunately this doesn't seem to work because I needed to insert the L flag after all rules, which causes the processing to stop when the rule is met. So, if the http-to-https rule is inserted first in htaccess, this rule is met and user specific rule is ignored (and viceversa for other case). Is there a way to both:
handle users requests with a specific rule
force users requests to be sent over HTTPS
with htaccess rules?

For many reasons my advice is to :
- do 301 redirect to https on all http requests
- use HSTS on all https answer
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}
or
<VirtualHost *:80>
ServerAlias *
RewriteEngine On
RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [redirect=301]
</VirtualHost>
And in the 443 Virtualhost :
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
WARNING : Set the Strict-Transport-Security ONLY when the https works on all webpage. You can try with 3600 (10 minutes) in the begining if you are not sure. The "includeSubDomains" will for https for all subdomains and "preload" will allow your website to be added to the preload list of browsers.
If you have a login page do not forget the Secure attribute on the cookies (without it the cookie will be sent with insecure http request too).
Why ?
- Because without HSTS someone can force the browser to do a request to http://example.com/mysecurepage and then intercept the http request before you answer a 301, and respond the same page than the one you respond, but with http links instead. This attack is called sslstrip and the only thing that can prevent it is HSTS (preloaded if possible)
- With HSTS protect you if you forgot to add the secure attribute on cookies (but not all browser know HSTS, that's why it's still important to use the secure attribute!)
- Because it's easy to miss which pages must be mark secure :
if a page ask for personal data (email, name, ...) then in most European country you have the obligation to secure it ; sometimes, a page do not ask personal data but display it ; sometime the fact that the content of the page is public do not mean the visitor accept that anyone know he visits it.
- With HTTP2 and SPDY encrypted page can be served faster than not encrypted one: https://istlsfastyet.com/

Related

How to set HSTS header from .htaccess to force loading assets via https

After switching to https and adding following lines to .htaccess
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{HTTPS} !=on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
Header set Strict-Transport-Security "max-age=31536000" env=HTTPS
Everything seems to be working fine - even if someone type http://mywebsite.com will be redirected to https://.
However, as per my understanding, HSTS should also enforce all content to be loaded via https if possible. Unfortunately, every now and then if someone makes a mistake and load some image (hosted on the same domain) via http:// instead of https:// Chrome will show a mixed content warning.
Did I make some mistake or my understanding of HSTS is wrong?

301 Redirect from http to https same page name

checked the Forum but could not find an ideal answer. I have recently installed a SSL Certificate on my site and in the process of creating 301 redirects via the .htaccess file for nearly 400 page urls (to keep Google happy). I thought of using;
redirect 301 /contact.php https://www.mydomainname.co.uk/contact.php
but it breaks the site. The only solution I have seen is;
RewriteEngine on
RewriteCond %{HTTPS} off
RewriteRule ^contact\.php$ https://www.mydomainname.co.uk/contact.php [L,R=301]
The above seems a lot of code to use for each of the 400 pages! is there a quicker way with less code I can use in the .htaccess file?
Many thanks. Hope someone can advise.
There are two basic ways of redirecting pages with Apache: Redirect (of mod_alias) and RewriteRule etc. (of mod_rewrite).
Redirect is very simple: it will just redirect a single URL to another. It can be useful sometimes, but it's usefulness is limited to its simplicity: in the case of HTTP-to-HTTPS redirection, it can't differentiate between HTTP and HTTPS connections, so it will just try to redirect to HTTPS even if you're already on HTTPS (and thus you end up in an infinite redirect loop).
RewriteRule, on the other hand, is more advanced and flexible. You can use RewriteCond to conditionally redirect requests; in your case, you'd want to redirect requests only if they're on a HTTP connection.
As you mentioned, you want to redirect to HTTPS for many (I presume all) requests; you can easily do this with only a single rule:
# Enable rewrites
RewriteEngine on
# Only run next RewriteRule on HTTP connections (not HTTPS)
RewriteCond ${HTTPS} off
# Redirect any page to the same URL with https:// schema
RewriteRule (.*) https://${SERVER_NAME}/$1 [L,R=301]
(The ${SERVER_NAME} variable will automatically be equal to your domain name, so you can even use this on web servers with multiple domain names.)

SSL auto-detection for single directory (using X-Forwarded-Proto) not working

I'm sure this is something stupid, and I've just been looking at this too long.
I'm running Apache 2.4.10
I want a single directory to force the user into HTTPS. Outside of that directory, I want the user to be bounced back to standard HTTP (unless they are loading the support files for an HTTPS page, eg. images, css)
This is behind a load balancer, and the load balancer is handling the HTTPS, so I'm relying on the X-Forwarded-Proto header as all the requests come into the nodes with the same protocol and port otherwise. I've doublechecked, and the header IS being populated by the LB for both HTTP and HTTPS transactions.
This is what I have in my config:
RewriteCond %{HTTP:X-Forwarded-Proto} http [NC]
RewriteCond %{REQUEST_URI} ^/secureDir/(.*) [NC]
RewriteRule .* https://%{SERVER_NAME}%{REQUEST_URI} [R=301,L]
RewriteCond %{HTTP:X-Forwarded-Proto} https [NC]
RewriteCond %{REQUEST_URI} !^/secureDir/(.*) [NC]
RewriteCond %{REQUEST_URI} !^.*\.(gif|jpg|png|swf|css|js) [NC]
RewriteRule .* http://%{SERVER_NAME}%{REQUEST_URI} [R=301,L]
This is what I'm wanting, and what I THINK should be happening.
If the request came in over HTTP AND the request is for a resource in "/secureDir/" - the request will be bounced to HTTPS (at the same REQUEST_URI).
If the request came in over HTTPS and the request is NOT inside "/secureDir/" it will be bounced to HTTP (unless it is just a request for gif|jpg|etc).
This should seamlessly move a user back and forth between HTTPS and HTTP if they move in and out of the secureDir directory ... BUT it is not.
If I hit the secureDir directory, it is not automatically putting me in HTTPS. If I manually request as HTTPS it will put the request through. If I move out of the secureDir directory, it will automatically put me back into HTTP. So, it seems the 2nd set of Rewrite conditions are catching, but not the first.
I've also tried alternating the conditions to look for the inverse (!http, !https - but I still seem to only get one catch)
Any insight as to what I'm overlooking?
Found the error. There was an unterminated rewrite a few lines prior, so it was appending those conditions to the first set of conditions in this group.
For anyone else who may be trying to do a similar thing - the only other thing I changed was to change the "http" match for X-Forwarded-Proto to "^http$" to make sure it wasn't greedy (as I got some 'infinite redirect' conditions if I didn't)

Apache .htaccess convert http uri to https causing redirect loop

I know this question has been asked a thousand times, but I cannot seem to find the answer.
We have a website hosted by 123-reg's shared web hosting package (no access to http config files). I have added ssl to the site, and the certificate works when directly requesting using https.
The problem arises when I try to redirect everything from http to https using the .htaccess file.
First I tried the SERVER_PORT variable in the condition:
RewriteEngine On
RewriteBase /
RewriteCond %{SERVER_PORT} 80
RewriteRule ^(.*)$ https://example.co.uk/$1/ [R=301,L]
This does not work as the https redirect request uses port 80 also (I am querying this with 123-reg at the moment). The condition is always met and causes a redirect loop.
Next I tried the HTTPS variable:
RewriteEngine On
RewriteBase /
RewriteCond %{HTTPS} !on
RewriteRule ^(.*)$ https://example.co.uk/$1/ [R=301,L]
This condition is always met as the variable is never set to on (and causes a redirect loop). I wonder if this is to do with the port no = 80 for https.
I found two server variables, SSL and HTTP_X_FORWARDED_SSL, which do change from "" to 1, but only when I delete the .htaccess file and directly request http or https.
If I try and use the SSL or HTTP_X_FORWARDED_SSL variables in the RewriteCond condition, it causes a redirect loop.
I cannot see the variables while the redirect loop is happening, so I do not know if they are being changed during the re-direct.
edit:
I have found the answer. I was using:
RewriteCond %{SSL} !1
which should be:
RewriteCond %{ENV:SSL} !1

Difference between 2 apache mod_rewrites

I've found 2 different code snippets to force https on my website:
RewriteRule (.*) https://%{SERVER_NAME}/$1 [R,L]
and
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}
I'm sure that they both work (one's from Httpd Wiki and the other's from SSL shopper). Would someone be able to explain the differences in how they perform the redirect?
They just use different Apache variables to make up the URL for redirect.
RewriteRule (.*) https://%{SERVER_NAME}/$1 [R,L]
This first rule takes the filename if one is entered such as myfile.php and appends the redirect with it replacing $1 in the redirect so that you get https://somesite.com/myfile.php
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}
The 2nd one using %{HTTP_HOST} will grab the information from the http headers instead to make up the URL used to redirect so entering the same url http://somesite.com/myfile.php will be redirected to https://somesite.com/myfile.php
It's just a matter of telling apache what to use for redirection. Either use the server internal name or use the one sent by the browser.
%{SERVER_NAME}
That is a server internal variable in apache and is defined in the server config.
%{HTTP_HOST}
This is the what is sent by the browser in the HTTP request headers. This is client side while the SERVER_NAME if from the server config.
%{REQUEST_URI}
REQUEST_URI is the path component of the requested URI, such as "/index.html". This is a special Apache variable.
There a many ways that have been done to redirect to https and both should work. Your choice.