All hosting help

Redirect HTTP to HTTPS

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

Updated September 2026

Once your site works over HTTPS, use a redirect to send visitors from HTTP to HTTPS. Choose the rule that matches your preferred hostname and proxy configuration.

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)

# 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)

# 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

# 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]

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. 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. Or, check X-Forwarded-Proto instead of HTTPS. This works in any SSL mode but is a looser check.
# 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:

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:

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

Verify the redirect

# 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 issues

  • 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.

Contact support

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

Related reading