Skip to content

How to Redirect HTTP to HTTPS with .htaccess (301 Redirect)

Updated 6 min read

Updated: 18 October 2026 · Applies to: Apache 2.4 (tested with 2.4.66 on Ubuntu 26.04), LiteSpeed (according to LiteSpeed), cPanel 138 and later, Plesk Obsidian and Virtualmin

To redirect all HTTP traffic to HTTPS, add these three lines at the top of the .htaccess file in your website's folder (usually public_html):

RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

Every http:// request is then sent to the same address on https:// with a permanent 301 redirect, keeping the path and the query string. The domain needs a working SSL certificate first. On cPanel you can also switch on Force HTTPS Redirect instead of editing files (see below).

Step by step

  1. Check that HTTPS works. Open https://example.com in your browser. If it shows a certificate warning, fix that first, or the redirect sends every visitor to the warning page. On cPanel, AutoSSL issues a free certificate; see install an SSL certificate in cPanel.
  2. Open .htaccess. In cPanel, open File Manager, go to public_html and turn on hidden files (edit .htaccess in File Manager). Create the file if it does not exist. Keep a copy of the old content.
  3. Paste the three lines at the very top, above any other rules, including WordPress's # BEGIN WordPress block.
  4. Save and test with curl or a private browser window:
$ curl -sI http://example.com/page.html
HTTP/1.1 301 Moved Permanently
Date: Sun, 11 Oct 2026 15:02:59 GMT
Server: Apache/2.4.66 (Ubuntu)
Location: https://example.com/page.html

That is lab output from our Apache test server. curl -sIL follows the redirect and should end with HTTP/1.1 200 OK after exactly one 301. A request for http://example.com/page.html?utm=1&x=2 went to https://example.com/page.html?utm=1&x=2: mod_rewrite keeps the query string by itself.

What each line does

  • RewriteEngine On turns on mod_rewrite for this folder.
  • RewriteCond %{HTTPS} !=on applies the rule only when the request did not come in over HTTPS, so HTTPS requests are left alone (no loop).
  • RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} builds the same URL with https://.
  • R=301 makes it permanent, which tells search engines to index the HTTPS address. A plain R returned 302 (temporary) in our test. L stops processing further rules. For the differences between the codes, see 301, 302, 307 and 308 explained.

Why the rule must come first

We tested a WordPress .htaccess with the HTTPS rule placed below the WordPress block. Real files such as /page.html were redirected, but a post address such as http://example.com/hello-world/ loaded over plain HTTP with 200, because WordPress's [L] rule handled it first. With the HTTPS rule at the top, the same request got 301 to https://example.com/hello-world/. For the WordPress side (site address settings and mixed content), see force HTTPS for WordPress.

HTTPS and www (or non-www) in one redirect

To send everything to https://www.example.com with a single redirect:

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]

In our test, http://example.com, https://example.com and http://www.example.com each got one 301 to https://www.example.com/..., and https://www.example.com returned 200. To drop www instead, change the second line to RewriteCond %{HTTP_HOST} ^www\. [NC] and the last line to RewriteRule ^ https://%1%{REQUEST_URI} [L,R=301]; we tested that version the same way. Your certificate must cover both names.

Behind Cloudflare or another proxy

According to the Apache documentation, %{HTTPS} comes from mod_ssl, so when HTTPS ends at a proxy or load balancer, the server always sees it as off. We simulated this: a request carrying X-Forwarded-Proto: https still got a 301, which in a browser becomes ERR_TOO_MANY_REDIRECTS. If you run your own proxy that sets that header, use:

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

This returned 200 for proxied HTTPS requests and 301 for plain HTTP ones. Apache warns that visitors can forge this header, so rely on it only when your proxy sets it on every request. On Cloudflare, Cloudflare recommends letting its Always Use HTTPS setting (SSL/TLS, Edge Certificates) do the redirect, and says a redirect at the origin loops when the encryption mode is Flexible: use Full or Full (strict) with a certificate on your server.

On a VPS or dedicated server: the Apache configuration

If you can edit the Apache configuration, the Apache documentation recommends a Redirect in the port 80 virtual host instead of mod_rewrite:

<VirtualHost *:80>
    ServerName example.com
    ServerAlias www.example.com
    Redirect permanent / https://example.com/
</VirtualHost>

In our test this sent /page.html?x=1 to https://example.com/page.html?x=1, also from the www name. Check the syntax with sudo apachectl configtest, then reload Apache (apache2 on Debian and Ubuntu, httpd on AlmaLinux and Rocky Linux). Nginx does not read .htaccess at all: see redirect HTTP to HTTPS in Nginx.

Without editing files: control panels

  • cPanel: Domains, Domains, then the Force HTTPS Redirect switch next to the domain. According to cPanel, it is only available for a domain with a valid SSL certificate and does not appear for aliases.
  • LiteSpeed: on cPanel servers that run LiteSpeed Web Server, LiteSpeed says it reads Apache configuration and rewrite rules, so the .htaccess lines above work as they are.
  • Plesk: Websites & Domains, Hosting Settings, Permanent SEO-safe 301 redirect from HTTP to HTTPS. According to Plesk, it is on by default and needs SSL/TLS support enabled.
  • Virtualmin (used on our shared hosting): according to Virtualmin's documentation, Web Configuration, Website Redirects can add a redirect for / to the same URL with the new protocol, applied to the non-SSL website only.

Troubleshooting

  • 500 Internal Server Error right after saving: the error log showed Invalid command 'RewriteEngine', perhaps misspelled or defined by a module not included in the server configuration when mod_rewrite was off. On your own Debian or Ubuntu server, run sudo a2enmod rewrite and reload Apache; on hosting, remove the lines and ask support.
  • Nothing happens: with AllowOverride None for the folder, Apache ignored the file and answered 200. Allow overrides for that directory (AllowOverride All), or check whether the site runs on Nginx.
  • Too many redirects: a proxy in front (see above) or two rules fighting, for example www added by one rule and removed by another. curl -sIL http://example.com shows each step.
  • Forms or API calls stop working: a 301 turns a POST into a GET; curl did exactly that in our test, and the GET can end in a 405 Method Not Allowed. Point forms and API clients at the https:// URL. With R=308 instead of R=301 our test server answered 308, which tells clients to keep the method.
  • Browser still goes to the old address: browsers cache 301 redirects, so test in a private window or with curl.

Once every page loads over HTTPS without warnings, you can add HSTS so browsers always use HTTPS (for WordPress sites, see enable HSTS).

Official documentation: Apache: redirecting and remapping (Forcing HTTPS), cPanel Domains and Cloudflare Always Use HTTPS.

Related services

Managed cPanel VPS

cPanel included; we handle updates, security and monitoring.

cPanel server management

hardening, migrations and 24/7 monitoring, with no long-term contract.

cPanel, Plesk and LiteSpeed licences

for VPS and dedicated servers, with installation help on demand.

Was this answer helpful?

Still stuck? Open a ticket