dotvitals

Plain HTTP does not redirect to HTTPS

HighConfirmedQuick winhttp.reachability.http-open-no-redirect

What this check looks for

The insecure port serves the site instead of sending visitors to the secure one. Anyone typing your address without https:// gets an unencrypted page.

Why it matters

The first request a visitor makes is almost always plaintext — typed addresses, old bookmarks, links in emails. Without a redirect that request travels in the clear, and anything on the path can read it or change it before HSTS ever gets a chance to apply.

When the check passes, your report says: “Plain HTTP redirects visitors to the secure address”.

What it costs your score

When this check fails it removes 30 points from your HTTP score, before the status, confidence and repeat multipliers are applied. HTTP carries a weight of 7 in the overall score.

It shares the http.reachability family ceiling of 60 points: however many findings that family produces, together they cannot remove more than that from HTTP. One underlying problem showing up in several places is still one problem.

Severity
high
Default confidence
confirmed
Status when triggered
fail
Deduction
30 points
Family cap
http.reachability · 60
Category
HTTP
Module
Http reachability
Fix owned by
user
In the ruleset since
2026.09

How the whole score is calculated

How to fix it

Redirect every plain HTTP request to the same URL on HTTPS with a 301.

Without it, the first request of every visit is unencrypted and interceptable.

  1. Add a port-80 server block that redirects to https:// on the same host and path.

  2. Use 301 (or 308 to preserve the method), not 302 — a temporary redirect is re-requested every time.

  3. Preserve the path and query string; a blanket redirect to the home page loses deep links.

  4. Once the redirect is in place, add HSTS so browsers stop making the plaintext request at all.

How to confirm it worked

  • curl -sSI http://‹host›/ — expect '301' and a Location beginning https://‹host›/

A named slot like ‹domain› — and the braces left in the configuration below — is filled in with your own values when this rule appears on a report.

Remediation by platform

nginx
server {
	listen 80;
	listen [::]:80;
	server_name ‹host›;

	# Keep the ACME challenge on plain HTTP, or renewal breaks.
	location ^~ /.well-known/acme-challenge/ {
		root /var/www/html;
	}

	location / {
		return 301 https://$host$request_uri;
	}
}
Apache
<VirtualHost *:80>
	ServerName ‹host›
	RedirectMatch 301 ^/(?!\.well-known/acme-challenge/)(.*)$ https://‹host›/$1
</VirtualHost>
Caddy
# Caddy redirects HTTP to HTTPS automatically for any site it has a certificate for.
# An explicit block is only needed to change the status code:
http://‹host› {
	redir https://‹host›{uri} permanent
}
Cloudflare
SSL/TLS → Edge Certificates → Always Use HTTPS → On.
  • Use return, not rewrite: rewrite re-enters location matching and is easy to make loop.

  • Shown for Apache because the response identified Apache. It belongs in the virtual host, not in .htaccess — .htaccess is read on every request and cannot see the port-80/443 distinction reliably behind a proxy.

  • This runs at Cloudflare's edge and applies only to traffic that reaches visitors through it. An origin that is reachable directly — by IP, or through a DNS record that is not proxied — still answers on port 80 exactly as before. Configure the redirect on the origin as well.

Technical detail

http://‹host›/ returned HTTP ‹status›. ‹detail›

The redirect must be a permanent one (301 or 308) to the same path on HTTPS. Two details matter: redirect to the *same* host and path first, because a redirect that also changes host means the browser never sees an HSTS header for the original name; and keep /.well-known/acme-challenge/ served over plain HTTP, or certificate renewal breaks.

Standards and references

Test this on your domain

Run the check that produces this finding, on its own, against any domain.

Open the http reachability checkerBuild the fix

Other http reachability checks