Search code examples
.htaccesshttpmod-rewritehttpscloudflare

Why doesn't my .htaccess file redirect to https? How to fix?


Oh Jesus, I've tried this a million times, a million different ways using Stackoverflow, using blogs, my web host even did it at one point, to no success, or maybe my fiddling with caching broke it.

I want to redirect all HTTP:// traffic to https, it's detering folk from buying.

So this is the content of the file, what needs to change???

AddType text/html .shtml
AddHandler server-parsed .shtml
Options Indexes FollowSymLinks Includes
AddHandler server-parsed .html .htm
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/xml
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE application/xhtml+xml
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/x-javascript
AddOutputFilter DEFLATE .shtml
</IfModule>

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{SERVER_PORT} 80
RewriteRule ^(.*)$ https://www.pottertour.co.uk/$1 [R,L]
</IfModule>

<IfModule mod_expires.c>
    ExpiresActive On
    ExpiresDefault "access plus 10 days"
    ExpiresByType text/html "access plus 1 day"
    ExpiresByType text/css "access plus 0 day"
    ExpiresByType text/plain "access plus 1 month"
    ExpiresByType image/gif "access plus 1 month"
    ExpiresByType image/png "access plus 1 month"
    ExpiresByType image/jpeg "access plus 1 month"
    ExpiresByType image/jpg "access plus 1 month"
    ExpiresByType image/webp "access plus 1 month"  
    ExpiresByType application/x-javascript "access plus 1 month"
    ExpiresByType application/javascript "access plus 1 week"
    ExpiresByType application/x-icon "access plus 1 year"
</IfModule>
# END EXPIRES

OK I tried removing the <IfModule mod_rewrite.c> wrappers as per MrWhite's comment, and it seemed to put the loading in a constant loop. So I returned it to what it was previously, but without the wrappers. As per below.

RewriteEngine On
RewriteBase /
RewriteCond %{HTTP_HOST} ^pottertour.co.uk [NC]   
RewriteRule ^(.*)$ https://www.pottertour.co.uk/$1 [L,R=301]   
  • It's now blocking the loading of my google fonts and styles.css. Really CRITICAL!
  • It also fails to redirect.

Solutions and explanations well appreciated.

OK well thanks to MrWhite I've had another go, the whole .htacess file now stands at this:

AddType text/html .shtml
AddHandler server-parsed .shtml
Options Indexes FollowSymLinks Includes
AddHandler server-parsed .html .htm
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/xml
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE application/xhtml+xml
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/x-javascript
AddOutputFilter DEFLATE .shtml
</IfModule> 

<IfModule mod_expires.c>
    ExpiresActive On
    ExpiresDefault "access plus 10 days"
    ExpiresByType text/html "access plus 1 day"
    ExpiresByType text/css "access plus 10 day"
    ExpiresByType text/plain "access plus 1 month"
    ExpiresByType image/gif "access plus 1 month"
    ExpiresByType image/png "access plus 1 month"
    ExpiresByType image/jpeg "access plus 1 month"
    ExpiresByType image/jpg "access plus 1 month"
    ExpiresByType image/webp "access plus 1 month"  
    ExpiresByType application/x-javascript "access plus 1 month"
    ExpiresByType application/javascript "access plus 1 week"
    ExpiresByType application/x-icon "access plus 1 year"
</IfModule>
# END EXPIRES

RewriteEngine On
RewriteCond %{HTTP:X-Forwarded-Proto} =http
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R,L]

Note I've removed all references to a permanent redirect by omitting the R=301

I've set a canonical https:// URL on all pages anyway, so I think adding to .htaccess doesn't help much.

It's still not accessing my styles.css or downloading google fonts. Nor is the browser executing on page javascript accordions correctly (how bizarre it's embedded on the page!)

I've contacted 1&1 my web host and asked them to ensure the .htacess file is 'refreshed'... I'll go off and research Cloudfare.


Solution

  • Having had a quick look at your site, it would seem you are using Cloudflare (which acts a proxy in front of your application server*1), in which case you would need to do something like the following instead to redirect HTTP to HTTPS:

    RewriteEngine On
    RewriteCond %{HTTP:X-Forwarded-Proto} =http
    RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
    

    You do not need the <IfModule> wrapper.

    You should first test with a 302 (temporary) redirect to avoid caching issues and only change to a 301 (permanent) redirect once you have confirmed it works as intended. 301s are cached persistently by the browser so can make testing problematic.

    You need to clear your browser cache before testing.

    This does not canonicalise the www/non-www subdomain.

    *1 Cloudflare acts as a front-end SSL proxy in front of your application server. This means that the connection from the client to the proxy is secure (ie. over HTTPS, when redirected), but the connection from the proxy to your application server is always HTTP. Which is why you can't detect the HTTPS connection on the application server itself and you end up getting a redirect loop. X-Forwarded-Proto is a header set/forwarded by the proxy to inform the backend/application server of the protocol used by the client.


    UPDATE: I've noticed that while the above redirect now works, you are only sending www subdomain requests through Cloudflare. Requests for the domain apex (ie. example.com) are going directly to your origin server (Apache) and are consequently not being redirected by the above rule.

    Everything (or nothing) should be sent through Cloudflare, so this is really a DNS config issue. However, we can resolve this by implementing a non-www to www redirect in .htaccess before the above HTTP to HTTPS redirect. (I'm assuming www is the preferred canonical URL?)

    For example:

    RewriteEngine On
    
    # Redirect non-www to www (and HTTPS)
    RewriteCond %{HTTP_HOST} !^www\.
    RewriteRule ^ https://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
    
    # Redirect HTTP to HTTPS (Cloudflare)
    RewriteCond %{HTTP:X-Forwarded-Proto} =http
    RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
    

    Note that the earlier rule (HTTP to HTTPS) is now second.

    This does assume you have no other subdomains. ie. example.com and www.example.com only. If you have other subdomains then you may decide you want to hardcode the domain in the first rule (similar to what you did earlier), for example:

    # Redirect non-www to www (and HTTPS) - domain hardcoded
    RewriteCond %{HTTP_HOST} =example.com
    RewriteRule ^ https://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L]