Redirect HTTP to HTTPS
Every HTTP URL on your site should end up on its HTTPS version in exactly one permanent redirect. Here is how to set that up, secure it with HSTS and verify the result.
Why you need an HTTP to HTTPS redirect
Installing a TLS certificate is only half the job. As long as http://example.com/ still answers with a normal 200 OK, you have two copies of every page: visitors can land on the unencrypted version through old links, bookmarks or by typing the domain without a scheme, and search engines see duplicate URLs that compete with each other.
A server-side redirect from HTTP to HTTPS solves both problems:
- Security: after the first request, all traffic is encrypted. Cookies, form data and logins are no longer sent in plain text.
- SEO: Google prefers HTTPS URLs as canonical and uses HTTPS as a lightweight ranking signal. A permanent redirect consolidates the signals of both variants on one URL.
- Browser warnings: browsers label HTTP pages as "Not secure", especially as soon as a page contains form fields.
- Modern features: HTTP/2 in browsers, service workers, geolocation and many other APIs only work in a secure context.
301 or 308?
Both status codes are permanent, and Google treats them the same way: they pass PageRank and are a strong signal that the target URL should be the canonical one. The difference is the request method. With a 301, clients are allowed to turn a POST into a GET when following the redirect; a 308 guarantees that method and body stay unchanged (see RFC 9110).
| Status code | Permanent | Method preserved | Typical use |
|---|---|---|---|
301 Moved Permanently | Yes | Not guaranteed | Default for HTTP to HTTPS, universally supported |
308 Permanent Redirect | Yes | Yes | APIs and forms that receive POST requests over HTTP |
302 / 307 | No | 302: no, 307: yes | Not suitable, only a weak canonicalization signal |
For normal websites, 301 is the pragmatic choice. If your HTTP endpoint receives POST requests, for example webhooks or an API, use 308 or, better, make clients call HTTPS directly. Details are in 307 vs. 308 and 301 vs. 302.
Server configuration
Apache (.htaccess)
This rule redirects every HTTP request to the same host and path over HTTPS. It requires mod_rewrite:
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
If Apache sits behind a load balancer or reverse proxy that terminates TLS, %{HTTPS} is always off and you get an endless loop. In that case, check the header set by the proxy instead:
RewriteEngine On
RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
More patterns are in the .htaccess redirect guide, or let the .htaccess generator write the rules for you.
nginx
In nginx, a dedicated server block for port 80 is cleaner and faster than an if inside the HTTPS block:
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
Using $host keeps the requested hostname. If you also want to normalize www, write the target host explicitly (see below). The nginx redirect guide and the nginx generator cover more cases. For other platforms, see the guides for Cloudflare, WordPress and IIS.
One hop, even with www
A very common mistake is chaining the HTTPS and the www redirect:
http://example.com/page
301 → https://example.com/page
301 → https://www.example.com/page
That is two round trips before the first byte of content, and a redirect chain that crawlers have to follow on every URL. Instead, every non-canonical variant should point straight to the final URL. In Apache:
RewriteEngine On
RewriteCond %{HTTPS} off [OR]
RewriteCond %{HTTP_HOST} !^www\.example\.com$ [NC]
RewriteRule ^ https://www.example.com%{REQUEST_URI} [L,R=301]
In nginx, both the port 80 block and an HTTPS block for the non-preferred host return the final URL:
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
return 301 https://www.example.com$request_uri;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name example.com;
ssl_certificate /etc/ssl/example.com/fullchain.pem;
ssl_certificate_key /etc/ssl/example.com/privkey.pem;
return 301 https://www.example.com$request_uri;
}
Make sure your certificate covers both hostnames, otherwise visitors of https://example.com see a certificate error before the redirect can happen. Choosing the preferred host is explained in the www redirect guide.
Exception: if you want to submit your domain to the HSTS preload list, the requirements say that HTTP must first redirect to HTTPS on the same host. For preloaded domains, http://example.com → https://example.com → https://www.example.com is intentional, because only this way the browser receives the HSTS header for the apex domain.
HSTS: make the browser remember HTTPS
The redirect alone leaves a small gap: the very first request still goes out over HTTP and could be intercepted. The Strict-Transport-Security response header closes that gap for returning visitors. Once a browser has seen it, it rewrites every http:// request for that host to https:// internally, without touching the network (see MDN: Strict-Transport-Security).
Apache (with mod_headers):
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
nginx (inside the HTTPS server block):
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
Browsers only accept the header on HTTPS responses; on HTTP it is ignored. Roll it out in steps:
- Start with a short
max-agesuch as300(five minutes) and withoutincludeSubDomains. - Check that every subdomain you use actually works over HTTPS, including internal tools, mail web interfaces and staging hosts.
- Increase to a week, then a month, then
31536000(one year). - Add
includeSubDomainsonly when you are sure that no subdomain needs HTTP.
The HSTS preload list and its risks
Browsers ship a built-in list of domains that are HTTPS-only from the very first visit. To be included, your header needs at least max-age=31536000, includeSubDomains and the preload directive, and you submit the domain via the preload list website run by the Chromium project.
Think carefully before you do this:
- Preloading applies to all subdomains, including ones you create in the future. Any subdomain without a valid certificate becomes unreachable.
- Removal is slow. It takes effect only with new browser releases, and users with older versions keep the entry for months.
- You cannot quickly fall back to HTTP if a certificate expires or a CDN misbehaves. Visitors cannot click through certificate errors on HSTS hosts.
For most sites, a normal HSTS header with a one-year max-age is enough. Preload is worth it if you control all subdomains and have automated certificate renewal.
Fix mixed content
After switching, pages loaded over HTTPS may still reference resources over http://: images, scripts, stylesheets, iframes or fonts. Browsers block active mixed content such as scripts entirely and either upgrade or block passive content such as images. The result is broken layouts, missing features and a warning in the address bar.
- Search your templates, database and CMS settings for hard-coded
http://URLs and replace them withhttps://or relative URLs. - Update the site URL in your CMS. In WordPress, set both the WordPress address and the site address to
https://. - Update canonical tags, hreflang links, Open Graph URLs and the XML sitemap to the HTTPS URLs.
- As a safety net, the
Content-Security-Policy: upgrade-insecure-requestsheader tells browsers to load subresources over HTTPS automatically.
Also update internal links. A redirect catches them, but every internal link that points to http:// costs an extra round trip.
Test your redirect
Enter http://example.com/ in the redirect checker and look at the result:
- The first hop should return
301or308, not302. - The redirect should lead directly to the final URL, with exactly one hop.
- The final URL should return
200. - Path and query string should be preserved:
http://example.com/a?b=1must end athttps://example.com/a?b=1, not at the home page.
Test all four combinations: http:// and https://, each with and without www. With the bulk checker you can check them in one go. The header checker shows whether the Strict-Transport-Security header is actually sent on HTTPS responses.
Frequently asked questions
- Should I use 301 or 308 for HTTP to HTTPS?
Both are permanent and Google treats them the same way.
301is the common default for websites. Use308if clients send POST requests to your HTTP URLs, because it guarantees that the method and body are preserved.- Does an HTTP to HTTPS redirect hurt my rankings?
No. A permanent redirect passes PageRank, and Google prefers HTTPS URLs as canonical anyway. Short fluctuations during recrawling are normal. What matters is that every URL redirects 1:1 to its HTTPS equivalent in a single hop.
- Is HSTS a replacement for the redirect?
No. Browsers only learn about HSTS from an HTTPS response, so first-time visitors and crawlers still need the server-side redirect. HSTS then protects returning visitors by skipping the insecure request entirely.
- Why does my HTTPS redirect cause ERR_TOO_MANY_REDIRECTS?
Usually TLS is terminated before your server, for example by a load balancer or by Cloudflare in "Flexible" mode. Your server then sees every request as HTTP and redirects again. Check
X-Forwarded-Protoinstead of%{HTTPS}, or switch Cloudflare to "Full (strict)". More in redirect chains and loops.
Related guides
-
www to non-www redirect (and vice versa)
www.example.com and example.com are two different hosts. Pick one, redirect the other to it permanently and make sure everything else on your site agrees.
-
Domain migration checklist
Moving a website to a new domain is manageable if every old URL redirects to its exact counterpart. This checklist walks you through before, during and after the move.
-
Redirect chains and redirect loops
Every extra redirect hop costs time, and a loop makes a page completely unreachable. Here is how chains and loops arise, how to find them and how to get rid of them.