Cart
Net 9 regions SYD 264 ms FRA 18 ms NRT 232 ms Uptime 30d 99.997 %

06 / SSL & security

Forcing HTTPS site-wide with .htaccess

Three .htaccess variants for redirecting HTTP to HTTPS, including the one that works behind Cloudflare without redirect loops.

5 min read · Updated April 2026

You have an SSL certificate. The site loads over https:// correctly. But visitors still land on http:// because Google indexed the old URLs and links around the web still use them. The fix is a 301 redirect in .htaccess. There are three correct versions depending on your setup.

Where to put the rule

The .htaccess file at the site root, the same directory as index.php. Edit via cPanel File Manager (click Settings → Show Hidden Files first) or via SFTP/SSH.

Variant 1: Simple HTTPS redirect (most sites)

apache
# Redirect all HTTP to HTTPS
RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

This is the one to start with. Put it at the top of .htaccess, above any WordPress block. Preserves host (works for both example.com and www.example.com) and the full path.

Variant 2: Force non-www + HTTPS (canonicalize)

apache
# Force https://yourdomain.com (no www)
RewriteEngine On
RewriteCond %{HTTPS} !=on [OR]
RewriteCond %{HTTP_HOST} ^www\. [NC]
RewriteCond %{HTTP_HOST} ^(?:www\.)?(.+)$ [NC]
RewriteRule ^ https://%1%{REQUEST_URI} [L,R=301]

Variant 3: Force www + HTTPS (the other way)

apache
# Force https://www.yourdomain.com
RewriteEngine On
RewriteCond %{HTTPS} !=on [OR]
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteCond %{HTTP_HOST} ^(?:www\.)?(.+)$ [NC]
RewriteRule ^ https://www.%1%{REQUEST_URI} [L,R=301]

Pick one of variants 2 or 3 and commit. Having both www and non-www available splits SEO signals. Google treats them as different sites unless one 301s to the other.

Behind Cloudflare: the redirect loop fix

If Cloudflare is in Flexible SSL mode, Apache sees all traffic as HTTP (because Cloudflare terminates TLS and talks to your origin over port 80). The HTTPS check fails, Apache redirects to HTTPS, Cloudflare rewrites that back to HTTP, infinite loop. Two fixes:

  1. 01. In Cloudflare → SSL/TLS, set the mode to Full or Full (strict). This tells Cloudflare to use HTTPS to your origin, and Apache sees HTTPS correctly.
  2. 02. Or, check X-Forwarded-Proto instead of HTTPS. This works in any SSL mode but is a looser check.
apache
# Variant 4: works behind Cloudflare in any SSL mode
RewriteEngine On
RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteCond %{HTTPS} !=on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

Protect the ACME challenge path

If you're still using HTTP-based AutoSSL, make sure the redirect rule excludes the challenge path or certificate renewal will break:

apache
RewriteEngine On
RewriteCond %{REQUEST_URI} !^/\.well-known/acme-challenge/
RewriteCond %{HTTPS} !=on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

Add HSTS once stable

After you've run on HTTPS for at least a week with no issues, add HSTS so browsers refuse to connect over plain HTTP in the future:

apache
<IfModule mod_headers.c>
    Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
</IfModule>

HSTS is sticky. Once a browser has seen the header, it enforces HTTPS for max-age seconds regardless of what you change later. Don't enable it until you're confident HTTPS is working on every subdomain.

Verify the redirect

bash
# Should return 301 with Location: https://...
curl -sI http://yourdomain.com/
# HTTP/1.1 301 Moved Permanently
# Location: https://yourdomain.com/

# Follow the redirect chain
curl -sIL http://yourdomain.com/ | grep -i 'HTTP/\|Location'

# Include www variant
curl -sI http://www.yourdomain.com/

Common gotchas

  • · Redirect loop behind Cloudflare Flexible. Fix: set Cloudflare SSL to Full or Full (strict).
  • · Rule placed below the WordPress block. Apache processes .htaccess top-to-bottom; the redirect rule must come first, before WordPress's index.php rewrites.
  • · 302 instead of 301. 302 tells search engines the move is temporary. Use 301 for a permanent change so link equity transfers.
  • · Mixed content after redirect. Site loads over HTTPS but images load over HTTP and break the padlock. Run a search-replace in the database: http://yourdomain.com → https://yourdomain.com.

Still stuck?

Email [email protected] with the output of curl -sI http://yourdomain.com/.

Support

Email [email protected] with your account email and the exact error. Direct support.