2

I want to force SSL on my website.

I have a bunch of rewrite rules in the web root / htaccess file needed for the live site, so I’ve isolated my testing to a /test folder with an htaccess file inside it.

In the test htaccess, I don’t have RewriteOptions Inherit so the root rewrite rules are not applied. To verify that, I added a rule in the root htaccess that rewrites test to a non existent page. If I remove the htaccess under test, I get the expected 404. If I add it back, I no longer get 404. Therefore, the root rule (and all the rest) is not applied and doesn’t mess with the SSL thing.


So here’s what I have in test/.htaccess:

RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=302]

When I navigate to example.com/test, I get redirected to https://example.com/test/ with error ERR_TOO_MANY_REDIRECTS.

However, If I use the query string as a flag to prevent multiple redirects:

RewriteEngine On
RewriteCond %{HTTPS} off
RewriteCond %{QUERY_STRING} !rd
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI}?rd [R=302]

I successfully get redirected from example.com/test to https://example.com/test/?rd with no errors and I can see the HTML page there.

It appears that this rule:

RewriteCond %{HTTPS} off

doesn’t work correctly.

Questions

What is the problem? How do I fix it? Is it some server configuration error?


Edit: When I var_dump($_SERVER['HTTPS']), I get NULL when on http and on when on https. It’s never off. However, even if I change the rule from:

RewriteCond %{HTTPS} off

to:

RewriteCond %{HTTPS} !=on

I still get the error.

Also, I’m testing with developer tools open, disabled cache and in incognito mode. That’s also the reason I’m using 302 redirect instead of 301 - to prevent caching.

Edit 2: Apache version is 2.2.22 and I’m not allowed to update it. This means I can’t use:

RewriteCond %{REQUEST_SCHEME} http$

as suggested.

Edit 3: I used Hurl.it to get the response headers from test under http:

Connection: keep-alive
Content-Encoding: gzip
Content-Length: 110
Content-Type: text/html; charset=UTF-8
Date: Mon, 05 Feb 2018 15:12:07 GMT
Server: nginx
Vary: Accept-Encoding
dodov
  • 5,206
  • 3
  • 34
  • 65
  • 1
    I'd dump out the value of it (in PHP, you can do `echo $_SERVER['HTTPS']`) to make sure the value is what it should be. If you're behind a load balancer, it may not be on as you expect. – ceejayoz Feb 05 '18 at 14:33
  • 1
    Doesn’t make sense that a query string parameter would influence this. Some browsers cache redirects really fiercely, so you can easily be fooled by outdated results, when testing such stuff. Suggest you open the browsers developer tools, network panel, and disable all caching first of all, then then try again. – CBroe Feb 05 '18 at 14:34
  • 2
    If `%{HTTPS} off` fails you can use `%{REQUEST_SCHEME} http$` on Apache 2.4* – Amit Verma Feb 05 '18 at 14:41
  • I’ve edited the post to answer all questions. The `HTTPS` value is correct, but that doesn’t help, redirects are not cached and I can’t update Apache to use the solution proposed by @starkeen. – dodov Feb 05 '18 at 14:50
  • 1
    Are you using load balancer? https://stackoverflow.com/questions/18328601/redirect-loop-while-redirecting-all-http-requests-to-https-using-htaccess/ – Amit Verma Feb 05 '18 at 14:53
  • Most probably you are behind some proxy or load balancer. Can you paste all the response headers in question. – anubhava Feb 05 '18 at 14:58
  • I just learned about load balancers... I don’t know if the server uses one. It’s hosted by Cloudways and I’m not the one that’s in control of the server or its configuration. I’ll ask the support team. @anubhava I updated the question to include response headers. – dodov Feb 05 '18 at 15:19
  • 1
    ok now take a note of `Server: nginx` which means you're running `Nginx` not `Apache` – anubhava Feb 05 '18 at 15:20
  • But when I intentionally cause an internal server error, I get `Apache/2.2.22 (Debian) Server at example.com Port 80` in the error page. Does that mean there are two servers at play, meaning that there _is_ a proxy? – dodov Feb 05 '18 at 15:23
  • It is possible that there is a Nginx reverse proxy in front of Apache – anubhava Feb 05 '18 at 15:28
  • 1
    Try replacing `RewriteCond %{HTTPS} !=on` with this `RewriteCond %{HTTP:X-Forwarded-Proto} !https` – anubhava Feb 05 '18 at 15:33
  • 1
    That did it! I even added it to the root htaccess and it works on the live websites too! Could you post this as an answer so I can mark it and maybe explain a bit _why_ it fixed the problem? Thank you! – dodov Feb 05 '18 at 15:54

1 Answers1

1

Since you are behind a proxy %{HTTPS} variable is not available.

To make it work, you can replace:

RewriteCond %{HTTPS} !=on

with this:

RewriteCond %{HTTP:X-Forwarded-Proto} !https
anubhava
  • 761,203
  • 64
  • 569
  • 643