Force HTTPS:redirect HTTP to HTTPS the right way
Force HTTPS with one clean 301 redirect in cPanel, .htaccess, Nginx or Cloudflare, update WordPress, add HSTS safely, and fix the redirect loop for good.

On this page
- What an HTTPS redirect actually does
- Before you start
- Pick one place to do the redirect
- Method 1: the cPanel Force HTTPS Redirect toggle
- Method 2: force HTTPS with .htaccess (Apache and LiteSpeed)
- Method 3: Nginx on a VPS
- Method 4: Apache virtual host (your own server)
- Method 5: Cloudflare
- Update WordPress so it stops creating http links
- Add HSTS once everything works
- How to check it worked
- Troubleshooting
- The one message worth forwarding
- Reader questions
- Sources & further reading
The short answer
Install a valid SSL certificate first, then send every http request to the same https address with one permanent 301 redirect, set in a single place: the cPanel Force HTTPS Redirect toggle, .htaccess, Nginx, or Cloudflare. Update the WordPress site addresses to https, test with curl -I, and add HSTS only once everything works. If you use Cloudflare, choose Full (strict), never Flexible, to avoid a redirect loop.
To force HTTPS, you send every http:// request to the same page on https:// with a permanent 301 redirect, and you do it in one place only: your hosting panel's toggle, a few lines in .htaccess, your Nginx config, or Cloudflare. Before any of that, the site needs a working SSL certificate, or the redirect simply sends visitors to a security warning.
Picture this (a made-up example). Sara launches her bakery website, installs a certificate, sees the padlock and celebrates. Two weeks later a customer types the address into her phone without the https://, and lands on the plain version marked "Not secure", right next to the order form. The certificate was fine. The redirect was missing.
By the end of this guide you will have HTTPS forced on cPanel, Apache, LiteSpeed, Nginx or Cloudflare, WordPress updated to match, a way to prove it works with one command, and the fix for the redirect loop that catches almost everyone once.
What an HTTPS redirect actually does
When someone opens http://example.com/menu, the server answers with a short reply instead of the page: status 301 Moved Permanently plus a Location header pointing to https://example.com/menu. The browser follows it straight away, so the visitor only sees the secure page.
Think of it as the forwarding notice on the door of a shop that moved one street over. Anyone who walks to the old door is sent to the new one, and they still end up exactly where they wanted to go.
Three details matter more than people expect:
- Use 301, not 302. A 301 says "this move is permanent". Apache's rewrite engine sends a temporary 302 unless you ask for 301 explicitly, so the code below always says
R=301. - Keep the path and the query string.
http://example.com/shop?item=12should land onhttps://example.com/shop?item=12, not on the homepage. - One hop is the goal.
http://www.example.comshould reach your final address in a single redirect, not bounce through two or three.
Why this matters: Google's own site-move guidance recommends permanent server-side redirects such as 301 or 308, and says to keep them "for as long as possible, generally at least 1 year" so signals pass to the new URLs.
Before you start
Run through this list first. It takes two minutes and saves an hour of confusion.
- A valid certificate on every name you will redirect. Open
https://example.comandhttps://www.example.comin a browser. Both must load with a padlock and no warning. Ifwwwshows a certificate error, fix the certificate first. - Access to where the redirect will live. For cPanel or
.htaccessyou need your hosting login (File Manager is enough). For Nginx or an Apache virtual host you need SSH with sudo rights. - A backup of .htaccess if it exists. One typo in that file produces an instant 500 error, and the backup is your undo button.
- Your one final address. Decide now:
https://example.comorhttps://www.example.com. Either is fine. Mixing them is not. - Whether a proxy sits in front of your server. Cloudflare, a load balancer or a CDN changes how the server sees HTTPS, and it is the number one cause of redirect loops.
- Time and risk. About 10 to 20 minutes. Low risk if you keep the backup and test right after saving.
Pick one place to do the redirect
You can force HTTPS at several layers. Pick one main place, because two layers redirecting in different ways is the classic recipe for ERR_TOO_MANY_REDIRECTS.
- cPanel Force HTTPS Redirect toggle: the easiest option on shared hosting, if your main domain already has a valid certificate.
- The .htaccess file on Apache or LiteSpeed: the most common choice on shared hosting when you also want to control
www. - Nginx server block: for a VPS that runs Nginx.
- Apache virtual host: for a server you manage yourself. The Apache documentation calls a
Redirectin a dedicated HTTP virtual host the cleanest approach. - Cloudflare Always Use HTTPS: if your domain is proxied through Cloudflare. Cloudflare recommends avoiding extra redirects at your origin server to prevent loops.
Quick tip: if you are on shared hosting and unsure, start with Method 1 or Method 2 below, then add WordPress and HSTS on top.
Method 1: the cPanel Force HTTPS Redirect toggle
This is a switch, no code involved.
Step 1: Open the Domains page
Log in to cPanel and open cPanel > Domains. You will see a table listing your domains, with a Force HTTPS Redirect column.
Step 2: Turn the toggle on
Find your domain and switch Force HTTPS Redirect to On. cPanel can also do several domains at once with the checkboxes and the bulk buttons at the top of the table.
Heads up: according to the cPanel documentation, you can only enable this on a main domain that has a valid SSL certificate, and the toggle does not appear for aliases. If it is greyed out or missing, the certificate is usually the reason, and your host can confirm.
Step 3: Test it
Open a private browser window and type http:// plus your domain. You should land on the https:// version. The full test is in the "How to check it worked" section below.
Method 2: force HTTPS with .htaccess (Apache and LiteSpeed)
.htaccess is a small text file in your website folder (usually public_html) that gives the web server extra instructions. Apache reads it, and LiteSpeed understands the same mod_rewrite rules. The name starts with a dot, so File Manager hides it until you enable the option to show hidden files in its settings.
Step 1: Back up the file
In File Manager, copy .htaccess to .htaccess.bak. If you use SSH, this command makes a dated copy in the same folder.
cp .htaccess .htaccess.bak-$(date +%F)If there is no .htaccess yet, simply create a new file with that exact name.
Step 2: Add the basic HTTPS rule at the very top
Open .htaccess and paste these lines at the top, above everything else, including any # BEGIN WordPress block.
# Force HTTPS
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301,NE]Here is what each line does, in plain words:
RewriteEngine Onswitches on the rewrite engine for this folder.RewriteCond %{HTTPS} offmeans "only continue if this request did not arrive over HTTPS". Apache sets theHTTPSvariable toonfor secure connections andoffotherwise.RewriteRule ^ ...matches every request and sends it tohttps://, the same host name, and the same path.R=301makes it permanent,Lstops processing further rules for this request, andNEstops Apache from re-escaping characters such as#or?in the target address.
Why %{REQUEST_URI} and not $1? Inside .htaccess, the path that a RewriteRule pattern sees never starts with a slash, so the popular https://%{SERVER_NAME}$1 pattern can glue the domain and the path together without the slash. %{REQUEST_URI} always includes the leading slash. The query string after ? is copied across automatically, because Apache keeps it whenever the target address has none of its own.
Why at the top? WordPress rewrites everything between its # BEGIN WordPress and # END WordPress markers when, for example, you save the permalink settings. Anything you put inside those markers can disappear. Anything above them stays.
Step 3: Choose www or non-www in the same redirect
If you also want to fix www, use one combined rule so visitors get a single hop. Replace example.com with your domain, and keep only one of the two versions.
This version sends everything to https://example.com (no www):
# Force HTTPS and remove www
RewriteEngine On
RewriteCond %{HTTPS} off [OR]
RewriteCond %{HTTP_HOST} ^www\.example\.com$ [NC]
RewriteRule ^ https://example.com%{REQUEST_URI} [L,R=301,NE]This version sends everything to https://www.example.com:
# Force HTTPS and add www
RewriteEngine On
RewriteCond %{HTTPS} off [OR]
RewriteCond %{HTTP_HOST} ^example\.com$ [NC]
RewriteRule ^ https://www.example.com%{REQUEST_URI} [L,R=301,NE]The [OR] flag joins the two conditions, so the rule fires when the request is not secure, or when it uses the host name you do not want. NC makes the host match case-insensitive. The dots are written as \. because a plain dot means "any character" in these patterns.
Heads up: if a subdomain such as shop.example.com shares this same folder, test it after the change. The rules above only touch the two exact names you typed, which is why they name the domain instead of using a catch-all pattern.
Step 4: Behind Cloudflare, a load balancer or a proxy
When HTTPS ends at a proxy and the proxy talks to your server over plain HTTP, Apache sees %{HTTPS} as off on every request. The basic rule then redirects forever, which is the loop you will meet in the troubleshooting section.
The Apache documentation's answer is to check the X-Forwarded-Proto header that the proxy adds instead:
# Force HTTPS behind a proxy that sets X-Forwarded-Proto
RewriteEngine On
RewriteCond %{HTTP:X-Forwarded-Proto} =http [NC]
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301,NE]Apache adds a warning worth repeating: only trust X-Forwarded-Proto if you control the proxy and it overwrites that header on every request, because anyone can send a fake one.
If the proxy is Cloudflare, there is a simpler path: set the encryption mode to Full (strict) (Method 5). Cloudflare then connects to your server over HTTPS, %{HTTPS} reads on, and the basic rule from Step 2 works normally.
Step 5: Save and test straight away
Save the file and reload your site. If you see a 500 Internal Server Error, restore .htaccess.bak immediately and check the file for a typo or a missing line. On OpenLiteSpeed (the free LiteSpeed edition often used on VPS servers), its documentation says rewrite changes need a server restart before they take effect.
Method 3: Nginx on a VPS
Nginx does not read .htaccess. You redirect inside the server configuration instead, and the Nginx documentation recommends a separate server block with return 301 rather than if and rewrite tricks.
Step 1: Add an HTTP server block that only redirects
This block catches every plain HTTP request for both names and sends it to your final address in one hop.
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
return 301 https://example.com$request_uri;
}$request_uri carries the full original path and query string, so /shop?item=12 survives the trip.
Step 2: Redirect https://www as well
This block sends secure www traffic to the bare domain. The certificate paths shown are where Certbot stores files for a certificate named example.com; use your own paths if they differ.
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
return 301 https://example.com$request_uri;
}Your main server block for example.com on port 443 stays as it is and serves the site.
Step 3: Test the config and reload
Always test before you reload, because a broken config can stop Nginx from starting.
sudo nginx -t
sudo systemctl reload nginxnginx -t should report that the syntax is ok and the test is successful. Only then reload.
Method 4: Apache virtual host (your own server)
If you manage the server, the Apache documentation's recommended pattern is a dedicated port 80 virtual host that does nothing but redirect.
<VirtualHost *:80>
ServerName example.com
ServerAlias www.example.com
Redirect permanent "/" "https://example.com/"
</VirtualHost>Redirect keeps the rest of the path automatically, so /menu goes to https://example.com/menu. Check the configuration, then reload Apache.
sudo apachectl configtest
sudo systemctl reload apache2On Red Hat style systems (AlmaLinux, Rocky Linux) the service is usually called httpd instead of apache2.
Method 5: Cloudflare
If your DNS records show the orange cloud (proxied), Cloudflare sits between visitors and your server, and this is where most redirect loops are born.
Step 1: Set the encryption mode to Full (strict)
Open SSL/TLS > Overview in the Cloudflare dashboard and choose Full (strict). Cloudflare then connects to your server over HTTPS and checks its certificate, which can come from a public authority such as Let's Encrypt or from Cloudflare Origin CA. Cloudflare strongly recommends Full or Full (strict).
Avoid Flexible if your server also redirects to HTTPS. In Flexible mode Cloudflare talks to your server over plain HTTP, your server answers "go to HTTPS", Cloudflare asks again over HTTP, and the loop never ends.
Step 2: Turn on Always Use HTTPS
Open SSL/TLS > Edge Certificates and switch on Always Use HTTPS. Cloudflare then redirects all http requests to https for every hostname on the domain, before they ever reach your server.
Quick tip: Always Use HTTPS does not fix mixed content. Pages that still load images or scripts from http:// addresses need the WordPress steps below.
Update WordPress so it stops creating http links
A redirect fixes the doorway. WordPress also needs to know its own address uses HTTPS, or it keeps writing http:// links into pages.
Step 1: Change the two site addresses
Go to Settings > General and change both WordPress Address (URL) and Site Address (URL) from http:// to https://, then save. WordPress may ask you to log in again afterwards, which is normal.
If both fields are greyed out, the addresses are set in wp-config.php with WP_HOME and WP_SITEURL, which override the dashboard. Edit those lines instead:
define( 'WP_HOME', 'https://example.com' );
define( 'WP_SITEURL', 'https://example.com' );Step 2: Or use the Site Health button
Since WordPress 5.7, Tools > Site Health can switch both addresses to HTTPS with one button once your hosting supports HTTPS. WordPress 5.7 also started replacing old http:// links to your own domain on the fly in post content, excerpts and widgets.
Step 3: Clean old http links out of the database
The on-the-fly replacement is a safety net, not a cleanup. To fix the stored links permanently, use WP-CLI over SSH. Take a database backup first.
wp db export before-https.sqlNext, preview what would change. The --dry-run flag runs the whole search and replace and shows a report without saving anything.
wp search-replace 'http://example.com' 'https://example.com' --skip-columns=guid --dry-runIf the report looks right, run the same command without --dry-run.
wp search-replace 'http://example.com' 'https://example.com' --skip-columns=guidwp search-replace handles PHP serialized data safely. The --skip-columns=guid part protects the GUID column, which the WordPress documentation says you should never change, because feed readers use it to track posts. No SSH? A reputable search-and-replace plugin can do the same job, and the same backup rule applies.
Step 4: WordPress behind a proxy
If your server sits behind a load balancer and the admin area loops or refuses to load, WordPress may not realise the visitor used HTTPS. The WordPress documentation suggests a check like this near the top of wp-config.php, above the line that says to stop editing. We added an isset check so PHP stays quiet when the header is missing.
define( 'FORCE_SSL_ADMIN', true );
if ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) && strpos( $_SERVER['HTTP_X_FORWARDED_PROTO'], 'https' ) !== false ) {
$_SERVER['HTTPS'] = 'on';
}The same trust rule applies here: only rely on this header when your own proxy sets it.
Add HSTS once everything works
A redirect only helps after the browser has already made one insecure request. HSTS (HTTP Strict Transport Security) is a response header that tells the browser "for the next N seconds, always use HTTPS for this site", so later visits skip the insecure step entirely.
Browsers ignore this header when it arrives over plain HTTP, so it only works once HTTPS is solid. Start small. hstspreload.org recommends ramping up in stages: 5 minutes (max-age=300), then 1 week (604800), then 1 month (2592000), checking for problems at each stage.
This .htaccess snippet sends the header with a 5-minute starting value:
<IfModule mod_headers.c>
Header always set Strict-Transport-Security "max-age=300; includeSubDomains"
</IfModule>On Nginx, put this inside the HTTPS server block. The always parameter adds the header to every response code, not only successful ones.
add_header Strict-Transport-Security "max-age=300; includeSubDomains" always;On Cloudflare, HSTS lives under SSL/TLS > Edge Certificates too, with Max Age options from 1 to 12 months.
Heads up: includeSubDomains forces HTTPS on every subdomain. If any subdomain cannot serve HTTPS yet, leave that part out until it can. And think hard before adding preload: it requires a max-age of at least 31536000 (one year) plus includeSubDomains, and hstspreload.org warns that inclusion in the preload list cannot easily be undone. Removal takes months to reach users.
How to check it worked
The fastest honest test is curl, because it shows you exactly what the server says, with no browser cache in the way. Run this on your computer. Windows 10 and 11 include curl too; in PowerShell type curl.exe instead of curl, because there curl is an alias for a different command.
curl -I http://example.com/You want a reply that starts like this:
HTTP/1.1 301 Moved Permanently
Location: https://example.com/Now test the awkward cases: a deep page with a query string, and the www name.
curl -I "http://www.example.com/shop?item=12"The Location line should read https://example.com/shop?item=12 (or your www version), with the path and query intact.
To count the hops, ask curl to follow every redirect and print only the status lines and locations:
curl -sIL "http://www.example.com/" | grep -iE "^(HTTP|location)"One 301 followed by one 200 is perfect. Two or more 301 lines mean a chain you can tidy up.
Finally, check the secure page itself. If you added HSTS, the strict-transport-security header should appear in this reply.
curl -I https://example.com/In the browser, open a private window, press F12, and look at the Network tab with Preserve log ticked, then reload the http:// address. The Console tab will list any mixed content warnings.
Troubleshooting
"This page isn't working: redirected you too many times" (ERR_TOO_MANY_REDIRECTS)
Two layers are fighting. The usual suspects, in order:
- Cloudflare in Flexible mode while your server also redirects to HTTPS. Switch to Full (strict).
- A proxy or load balancer in front of Apache, so
%{HTTPS}is alwaysoff. Use theX-Forwarded-Protorule from Method 2, Step 4. - WordPress still set to
http://in Settings > General while the server forceshttps://. Update both addresses. - An HTTPS-to-HTTP redirect somewhere (an old plugin, a Cloudflare redirect rule) fighting Always Use HTTPS. Remove the old one.
Clear the browser cache or use a private window after each fix, because browsers remember permanent redirects.
500 Internal Server Error right after saving .htaccess
The file has a typo, a stray character, or a line the server does not allow. Restore the backup first so the site comes back, then compare line by line. Your web server's error log (in cPanel under Metrics > Errors, if your host enables it) usually names the exact problem.
The address shows https but the padlock is missing or says "Not secure"
That is mixed content: the page loads over HTTPS but pulls some images, scripts or fonts over http://. Open the browser console to see which files, then run the WP-CLI search and replace from the WordPress section, and check your theme or page builder for hardcoded http:// addresses.
Certificate warning on www (NET::ERR_CERT_COMMON_NAME_INVALID)
Your certificate covers example.com but not www.example.com, or the other way round. Reissue it so it covers both names (cPanel AutoSSL or Let's Encrypt can include both), then test again.
The homepage redirects, but inner pages show 404 errors
The rule probably sits below the WordPress block, or a plugin rewrote the file. Move your HTTPS rule to the very top of .htaccess, then resave Settings > Permalinks once so WordPress rebuilds its own block.
The redirect goes to a strange host name
That happens with %{SERVER_NAME}, which can return the server's own name instead of your domain. The rules in this guide use %{HTTP_HOST} or your exact domain for that reason.
Nothing changes after you add the rule
Check that you edited the .htaccess in the folder that actually serves the site (an addon domain often has its own folder), that the file name is exactly .htaccess, and that you are testing with curl or a private window rather than a cached tab.
The one message worth forwarding
Copy this to your developer or hosting support:
"Hi, please make sure our site forces HTTPS with a single 301 redirect, so http:// and www both reach https://example.com in one hop with the path and query string kept. Please confirm the WordPress addresses use https, and if we use Cloudflare, that the SSL mode is Full (strict) so there is no redirect loop. Could you send me the output of curl -I http://example.com/ when it is done?"
The takeaway: a certificate gives you the padlock, but only a single, permanent, well-placed redirect makes sure every visitor actually sees it.
Reader questions
Will redirecting HTTP to HTTPS hurt my SEO?
Not when it is done properly. Google recommends permanent server-side redirects such as 301 or 308 and keeping them generally at least a year, plus updating internal links, canonical tags and your sitemap to the https addresses.
Should I use a 301 or a 302 redirect for HTTPS?
Use 301, because the move is permanent. Apache's rewrite engine sends a temporary 302 by default, so always write R=301 in the rule.
Do I still need the redirect if I turn on HSTS?
Yes. Browsers only accept the HSTS header over HTTPS, so a visitor's first request on plain http still needs the redirect to reach the secure site.
Can I force HTTPS in cPanel and in .htaccess at the same time?
Choose one main place. Two layers redirecting in different ways can create extra hops or a redirect loop.
Why do I get too many redirects after turning on Cloudflare?
Usually the SSL/TLS mode is Flexible, so Cloudflare talks to your server over http while your server keeps redirecting to https. Switch the mode to Full (strict) and test again in a private window.
Does forcing HTTPS fix mixed content warnings?
No. The redirect only changes the page address. Images, scripts or fonts still loaded from http addresses must be updated, for example with WP-CLI search-replace on WordPress.
How long should I keep the HTTPS redirect?
Keep it permanently if you can. Google's site-move guidance says to keep redirects for as long as possible, generally at least one year.
Sources & further reading
- Apache HTTP Server: Redirecting and Remapping, Forcing HTTPS recipe
- Apache HTTP Server: RewriteRule flags
- Apache HTTP Server: mod_rewrite reference
- cPanel documentation: Domains and Force HTTPS Redirect
- nginx documentation: Converting rewrite rules
- Cloudflare: ERR_TOO_MANY_REDIRECTS troubleshooting
- Cloudflare: SSL/TLS encryption modes
- MDN: Strict-Transport-Security header
- WP-CLI: wp search-replace
- Google Search Central: Site moves with URL changes
Originally published . About our editorial updates.


