Fixing HTTP to HTTPS Redirects
Every site on HTTPS still receives requests for its old http:// addresses, from bookmarks, old links, printed material and crawlers. Each of those should be sent to the matching https:// address in a single permanent redirect.
The Redirect Checker flags the ways this goes wrong: some addresses that still load over plain HTTP, upgrades that take two or three hops, a redirect that lands on a certificate error, or an upgrade that sends every page to the homepage. This guide covers building one clean upgrade rule, making certificates cover every hostname, working behind proxies, and adding HSTS without locking yourself out.
What a good HTTPS upgrade looks like
A healthy upgrade is boring. Any request to an http:// address gets one response: a 301
(or 308) whose Location is the final https:// address of the same
page, on your preferred hostname, with the path and query string unchanged. The next request returns
200.
curl -sI "http://example.co.uk/menu/?table=4" | grep -iE "^HTTP|^location"
HTTP/1.1 301 Moved Permanently
Location: https://www.example.co.uk/menu/?table=4
The Redirect Checker's HTTPS findings usually fall into one of these patterns:
| What the checker shows | What it means |
|---|---|
http:// address returns 200 | No upgrade rule at all for that host or path. The page is served insecurely and may be indexed twice. |
| Two or three hops before the 200 | The upgrade and the host rule are separate, or two layers both upgrade. |
| Every old URL lands on the homepage | The rule drops the path, so deep links and old backlinks
all arrive at /. |
| Upgrade uses 302 | A default somewhere is sending a temporary code for a permanent change. |
| Hop fails with a certificate error | The redirect goes to an https hostname the certificate does not cover. |
| The chain never ends | A loop, usually caused by a proxy connecting to the origin over HTTP. |
Pages that load over HTTPS but still pull images or scripts over HTTP are a separate problem, covered in fixing mixed content during redirects.
The four addresses to test
A domain usually answers on four combinations of scheme and host. All four should end at one address, and three of them should get there in one hop:
for u in http://example.co.uk/ http://www.example.co.uk/ \
https://example.co.uk/ https://www.example.co.uk/; do
echo "== $u"; curl -sIL --max-redirs 5 "$u" | grep -iE "^HTTP|^location"
done
Repeat the test with a deep path such as /contact/?ref=card, because some rules only
cover the homepage. If you run subdomains such as shop. or book., test those
too, since each needs its own rule and its own certificate coverage.
The one that most often breaks is https:// on the non-preferred host. The browser has
to complete a secure connection before the server can say anything, including "go elsewhere". If the
certificate only names www.example.co.uk, a visitor typing https://example.co.uk
sees a full-page security warning and never reaches your redirect.
The fix, step by step
Step 1
Test all four versions of the address
Run
the Redirect Checker, or the curl loop above, on http and https with and without www, first
for the homepage and then for a deep URL with a query string. Write down the hops for each. You are looking
for any version that returns 200 over plain HTTP, any certificate error, any 302, any version that takes
more than one hop, and any path or query string that gets dropped on the way.
Step 2
Get a certificate that covers every hostname
Check the certificate's subject alternative names in the browser's padlock menu or with
openssl s_client. It must list both the bare domain and www, plus any subdomains
you serve. Hosting panels usually have a tick box to include www when issuing a free certificate. Reissue
it if a name is missing, because a redirect from an uncovered https hostname can never be seen by visitors.
Step 3
Choose one layer to do the upgrade
Decide where the HTTP to HTTPS rule lives: the CDN, the web server, the hosting panel or the application. Pick one, ideally the outermost layer that terminates HTTPS, and switch the upgrade off everywhere else. Two layers both forcing HTTPS is the usual source of extra hops, and when a proxy talks to the origin over plain HTTP it is also the usual source of loops. Record the decision where the next developer will find it.
Step 4
Write a single rule that keeps the path
Write one rule that sends any HTTP request, on any of your hostnames, straight to https://
plus your preferred host plus the original path and query string, with a 301. Combining the scheme and
host change in one rule is what keeps it to one hop. In nginx that is return 301 https://www.example.co.uk$request_uri;
in the port 80 server block; in Apache it is a rewrite rule that matches both conditions.
Step 5
Fix upgrades behind a proxy or load balancer
If a CDN or load balancer handles HTTPS and forwards requests to your server over
HTTP, the origin sees every request as insecure. Either make the proxy connect over HTTPS with a valid
origin certificate, or have the origin read the X-Forwarded-Proto header and only redirect
when it says http. Only trust that header from your own proxy, otherwise anyone can set
it and change your application's behaviour.
Step 6
Add HSTS in stages
Once
every page, subdomain and asset works over HTTPS, send Strict-Transport-Security on HTTPS
responses, starting with a short max-age such as 300 seconds. Increase it to a day, a week,
a month and then a year as nothing breaks. Add includeSubDomains only when every subdomain,
including old ones used by staff tools or email services, has a working certificate. Consider preload
last, if at all.
Step 7
Update links, canonicals and Search Console
Change internal links, canonical tags, hreflang tags, structured data URLs and the XML
sitemap to use https:// addresses directly. Update the site address in your CMS so it stops
generating http links. In Search Console, use a domain property, which covers every scheme and host,
and submit the https sitemap. Then re-run the Redirect Checker on the four versions and your deep URL
to confirm one hop each.
Example rules
These examples assume the preferred address is https://www.example.co.uk. Swap the host
if you prefer the bare domain. Use only one of them, in the layer you chose.
nginx
server {
listen 80;
listen [::]:80;
server_name example.co.uk www.example.co.uk;
return 301 https://www.example.co.uk$request_uri;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name example.co.uk;
# certificate lines here, covering both names
return 301 https://www.example.co.uk$request_uri;
}
Apache (.htaccess or virtual host)
RewriteEngine On
RewriteCond %{HTTPS} off [OR]
RewriteCond %{HTTP_HOST} !^www\.example\.co\.uk$ [NC]
RewriteRule ^ https://www.example.co.uk%{REQUEST_URI} [R=301,L]
The [OR] joins the two conditions, so a request that is insecure, on the wrong host,
or both, is sent to the final address in one go. %{REQUEST_URI} keeps the path, and Apache
keeps the query string by default.
Application code behind a proxy
// Node.js with Express, behind one trusted proxy
app.set('trust proxy', 1);
app.use((req, res, next) => {
if (req.secure) return next();
res.redirect(301, 'https://www.example.co.uk' + req.originalUrl);
});
With trust proxy set, Express reads X-Forwarded-Proto from the proxy to
decide whether the request was secure. Without it, every request looks insecure and the redirect loops.
Configuration by panel and server is covered in more depth in the Apache
and nginx guide and the Plesk and
cPanel guide.
HSTS: what it adds and how to avoid a lock-out
A redirect only works after the insecure request has been sent, which means the first request travels unencrypted and could be tampered with. HSTS closes that gap for returning visitors. When a browser sees this header on an HTTPS response, it upgrades every future request for that host itself, before anything leaves the machine:
Strict-Transport-Security: max-age=31536000; includeSubDomains
In Chrome's Network panel the browser's own upgrade shows as 307 Internal Redirect. That
is not your server, and it will not appear in the Redirect Checker's server-side results. It also means
your server rule must stay in place: first-time visitors, crawlers and many tools never see the HSTS
header before their first request.
The risk is that HSTS is remembered for the whole max-age. If you set a year with includeSubDomains
and an old subdomain has no certificate, that subdomain becomes unreachable in browsers that have seen
the header, and lowering the value later only helps once those browsers come back and see the new header.
That is why the steps above raise the value gradually.
max-age, includeSubDomains and the preload token.
Getting removed from it is slow. Only apply once you are sure every current and future subdomain will
always support HTTPS.Edge cases
- The upgrade sends everything to the homepage. A rule that redirects to a fixed address without the path, or a hosting panel's "redirect domain" option pointed at the root, throws away deep links. Rewrite it to append the path.
- Forms that post to http addresses. An old form whose action is
http://gets a 301, and some browsers resend it as a GET, losing the submitted data. Update the form action; for API endpoints, a 308 keeps the method. - Non-standard ports. A rule built from the
Hostheader can carry a port such as:8080into the https address. Build the target from a fixed hostname instead. - Health checks and certificate renewal. Load balancer health checks and certificate
validation requests to
/.well-known/acme-challenge/may need to be served over HTTP. Most renewal tools follow a redirect to HTTPS, but some setups exclude that path from the upgrade rule. - Old domains. A second domain you own that forwards to the main site needs its own certificate if people might type it with https, and should go straight to the final address rather than through the main domain's upgrade.
Keeping the upgrade clean
- Keep the four-address curl loop in your deployment checklist and run it after any server, CDN or certificate change.
- Set up certificate renewal alerts, so an expired certificate on one hostname is caught before visitors see it.
- When you add a subdomain, add it to the certificate and the upgrade rule on day one.
- Store the site's own address with https in the CMS, environment variables and templates, so the site stops generating http links that rely on the redirect.
- Re-run the Redirect Checker on your key URLs every few months, and after any hosting move.
1. Why is my site still loading over HTTP?
Either no redirect rule exists for that combination of host and scheme, or the rule is in a layer the request never reaches. Common gaps are a rule that covers only www, a rule in an .htaccess file that the server is set to ignore, a CDN rule that is switched off, or a second domain alias with no rule at all. Test all four addresses with curl or the Redirect Checker to find which one answers with 200 over HTTP.
2. Should an HTTP to HTTPS redirect be 301 or 302?
Permanent, so a 301 or a 308. Moving to HTTPS is not something you plan to undo, and a permanent code tells search engines to index the secure addresses. Some tools and panels send a 302 by default, which still works for visitors but sends a less clear signal. Check the code the Redirect Checker reports for the first hop, and change it at the source if it is a 302 or 307 coming from your server.
3. Why does my redirect from https://example.com show a security warning?
Because the certificate does not include that hostname. The browser must finish a secure connection before it can read any redirect, so if the certificate lists only www.example.com, a visitor who types the bare domain with https sees a warning page first. Reissue the certificate with both names, which most hosting panels and free certificate tools support with a single option. Once the certificate covers the name, the redirect behind it starts to work.
4. Do I still need a server redirect if I use HSTS?
Yes. HSTS only takes effect after a browser has visited your site over HTTPS and received the header, so first visits, new devices, crawlers and many tools still request the http address and depend on your redirect. HSTS is an addition that protects returning visitors from the insecure first request. Keep the single-hop 301 in place permanently, and treat the header as a second layer of protection rather than a replacement for the redirect.
5. What does 307 Internal Redirect mean in Chrome?
It is Chrome upgrading the request to HTTPS by itself, because it has seen an HSTS header for your site before or the site is on the preload list. No request left the browser, so your server did not send a 307. It will not appear in server logs or in a server-side check like the Redirect Checker. To see what your server really does, use curl or a private window in a browser that has not visited the site.
6. Why does forcing HTTPS cause a redirect loop?
Usually because something between the visitor and your server handles HTTPS and then talks to your server over plain HTTP. Your server sees an insecure request and redirects to HTTPS, the proxy fetches over HTTP again, and it repeats until the browser gives up. Fix it by making the proxy connect over HTTPS, or by having your server read the X-Forwarded-Proto header from the proxy and only redirect when the original request was insecure.
7. Is it safe to submit my site to the HSTS preload list?
Only if you are certain every subdomain you have now, and every one you might add later, will always work over HTTPS. Preloading builds your policy into browsers, so a subdomain without a certificate becomes unreachable for everyone, and removal from the list takes a long time to reach users. Many small sites get most of the benefit from an ordinary HSTS header with a long max-age and never need to preload.
The Redirect Checker follows each URL through its full chain, recording every status code, Location header and hop time, and flags HTTPS gaps alongside long chains, loops, 302s that should be 301s and mixed content.
Open Redirect Fixes โ