How to check HTTP to https redirect
Request the http:// URL, follow the chain and print where it ended: curl -s -o /dev/null -L -w '%{num_redirects} %{scheme} %{url_effective}' http://example.com/. A working redirect reports one hop and a scheme of https. Zero hops with scheme http means the site answered the insecure request instead of moving it.
Why check this
This is the check that decides whether a link pasted into a chat window, a QR code or an old bookmark stays in clear text. Run it at staging sign-off, after every vhost or CDN change, and on any host that was recently added to a certificate. When it fails, a login form loads over HTTP, the browser posts the password in clear text, and nothing in the application log looks unusual because the server answered normally.
Prerequisites
- curl 8.0 or later, any build. See the curl manual.
- One URL with a path and a query string on it. The homepage can redirect correctly while deep links do not.
- Both hostnames, the apex and
www, since they are usually two separate server blocks.
Steps
- Step 1.
Follow the chain and print the hop count and the final URL in one line.
curl -s -o /dev/null -L -w 'redirects=%{num_redirects} scheme=%{scheme} code=%{response_code} url=%{url_effective}\n' http://www.cloudflare.com/redirects=1 scheme=https code=200 url=https://www.cloudflare.com/ - Step 2.
Run the same line against a host that serves plain HTTP, so the failing output is on record.
curl -s -o /dev/null -L -w 'redirects=%{num_redirects} scheme=%{scheme} code=%{response_code} url=%{url_effective}\n' http://httpbin.org/getredirects=0 scheme=http code=200 url=http://httpbin.org/getcode=200on anhttpscheme is the failure. A status of 200 here means the content was served, not that the check passed. - Step 3.
Print every hop with its status code, so you can see how many there are and which one adds the policy header.
curl -s -o /dev/null -L -D - http://cloudflare.com/ | grep -i -E '^HTTP/|^location|^strict-transport'HTTP/1.1 301 Moved Permanently Location: https://www.cloudflare.com/ HTTP/2 103 HTTP/2 200 strict-transport-security: max-age=31536000; includeSubDomainsThe
103line is an early hints response on the final hop, not a redirect. Only the301moved the client. - Step 4.
Confirm the path and the query survive the hop.
curl -s -o /dev/null -D - 'http://www.cloudflare.com/plans/?x=1' | grep -i -E '^HTTP/|^Location'HTTP/1.1 301 Moved Permanently Location: https://www.cloudflare.com/plans/?x=1A
Locationthat points athttps://www.cloudflare.com/with the path dropped would be the bug this step exists to catch.
How to read the result
| What you see | What it means | What to do |
| --- | --- | --- |
| redirects=1 scheme=https code=200 | One hop, correct scheme | Nothing. Repeat for the other hostname. |
| redirects=0 scheme=http code=200 | The site serves content over plain HTTP | Release blocker. Every form on that page posts in clear text. |
| redirects=2 or more | The scheme and the host change in separate hops | Collapse them. Each extra hop is another clear-text request if it stays on HTTP. |
| 302 instead of 301 on the upgrade | The move is advertised as temporary | Use 301. Caches and clients keep retrying HTTP for a temporary redirect. |
| Location loses the path | Deep links land on the homepage | Fix the rewrite rule. Password reset and invite links break on this. |
| redirects=0 code=000 | Port 80 refused the connection | Decide deliberately. A closed port is safe but breaks typed hostnames. |
Common mistakes
What to check next
- How to check if HSTS is enabled: the redirect fixes the second request, HSTS fixes the first.
- How to check if cookies are secure and HttpOnly: what is exposed on the one request that does go out over HTTP.
- How to check if mixed content exists on a page: the page can be reached over HTTPS and still pull assets over HTTP.
- How to check HTTP response headers with curl: the
-Dand-wflags used above, in detail. - Redirect chain checker: prints the same chain without a terminal.
FAQ
How to check if a site is served over HTTPS?
Step 1 answers it. %{scheme} prints the scheme of the URL curl actually ended on, which is the fact you want, rather than the scheme you typed.
How to check if HTTPS is working?
Request the https:// URL directly and read the status line. A TLS failure returns exit code 60 with no status line, which is a different problem from a missing redirect on port 80.
Should the redirect be 301 or 302?
301 for a permanent scheme upgrade. A 302 tells caches and clients to keep asking over HTTP next time, which leaves the insecure request in place on every visit.
Does closing port 80 count as a pass?
It removes the clear-text response, and it also removes the redirect. Users who type the hostname get a connection error instead of the site, so most teams keep port 80 open and serve only the 301.
Why does curl show one hop and the browser network panel shows none?
The browser upgraded the URL from its own HSTS store before sending anything. Test in a fresh profile, or trust the curl result.
Verified
Verified by Maks Vernycurl 8.21.0
Each output block is what the command above it printed on that date, on the host named in the step. Figures read from a live site move between runs. Compare the shape of the answer rather than the digits, and see the methodology for how a page is re-verified.
Related on this site
basic4 minpublished updated Maks Verny