Skip to content

Note

The http redirect a static host does not add for you

Certificates are automatic almost everywhere now. The redirect is not: port 80 and port 443 are configured in different places, and a host can answer both of them with a cheerful 200. The fix is one line, and the first step is a measurement anybody can take from outside.

  • https
  • Static hosting
  • Deployment

Written

Measure it before fixing it

One request answers the question. Ask for the http address and do not follow the redirect, so what comes back is the first response rather than the last one:

curl -sS -o /dev/null -D - http://example.com/

Three outcomes, and they are not near each other:

  • 301 or 308, with a Location on https. Correct. Nothing to do here.
  • 200, with a page body. The site is being served over plain http as well as over https. Everybody who typed the bare name, every old link and every bookmark is reading it unencrypted, and nothing on the page says so.
  • Connection refused, or a timeout. Port 80 is closed. Safer than the second case and worse than the first for a human: somebody who types the name into a browser that has not been to the site before gets an error instead of the site.

Why it is not automatic

The certificate and the redirect are two settings owned by two different layers. Issuing a certificate is a one-time ceremony against a name; answering port 80 is routing. A platform that automates the first has no obligation to do anything in particular about the second, and all three defensible defaults — redirect, serve, refuse — exist in the wild.

It is also the last thing anybody checks, because every route a developer takes to the site already begins with https: the dashboard link, the repository link, the browser's own autocomplete. The plain-http request is the one a stranger makes.

The fix, per platform

Four that cover most static hosting. In each case it is a setting or a few lines rather than a project:

  • Cloudflare, proxied. SSL/TLS, then Edge Certificates, then Always Use HTTPS — a switch for the whole zone — or a single redirect rule if it has to apply to one hostname only. Pages behind the same proxy inherits it.
  • Netlify. Domain management, HTTPS, Force HTTPS. It is a toggle rather than a _redirects rule, because the scheme is not something the redirect engine gets to see.
  • S3 behind CloudFront. Set the default cache behaviour's viewer protocol policy to redirect-to-https. An S3 website endpoint on its own speaks plain http and cannot do this at all, which is the reason the bucket is behind a distribution in the first place.
  • nginx. A server block whose only job is the redirect:
server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;

    location /.well-known/acme-challenge/ { root /var/www/acme; }
    location / { return 308 https://$host$request_uri; }
}

Leave the challenge path on port 80. That first location is not decoration. An http-01 certificate validation is an ordinary http request for a file under /.well-known/acme-challenge/, and although the ACME specification lets the validating server follow redirects, sending it into an https host that does not yet serve a valid certificate for the name is how a first issuance deadlocks. Redirect everything else.

301 or 308, decided once

Both are permanent redirects. The difference is what a client may do to the request method: 301 has always been widely treated as licence to turn a POST into a GET, and HTTP Semantics describes exactly that history as the reason 308 exists. 308 preserves the method and the body.

On a site that only serves GET requests this is theoretical. On anything with a form or an API on the same hostname it is not: a POST that silently becomes a GET arrives with no body, and the server answers with something confusing rather than something obviously wrong. Use 308 and stop thinking about it.

Then take the first request out of the equation

A redirect still means one plain-http request, which is one opportunity to intercept it. The Strict-Transport-Security header removes that for everybody who has been to the site before: for max-age seconds the browser remembers that this host is https-only and rewrites the scheme itself, before anything leaves the machine.

  • Send it over https only. A browser is required to ignore the header when it arrives over plain http, so putting it on the redirect response achieves nothing.
  • Start with a short max-age. It is a promise the browser holds you to. A long one on a host that later has to serve something over http is a self-inflicted outage with no quick undo.
  • includeSubDomains and preload are one-way doors. Preload ships inside browser binaries, so removal is a release cycle rather than a deploy. Add them only once every subdomain, including the ones somebody else operates, is certainly https.

In that order: measure, redirect with 308, keep the challenge path reachable, then shorten the window with a header. Each step is independently checkable from outside the host, which is the point — none of it has to be believed.

Sources

Where this is written down

Related

Where this comes up in the work

More notes

Other things worth writing down

Next step

Tell us the version, the hardware, and what it has to do.

You will get a written scope and a fixed price against it. If the honest answer is that you do not need us, you will get that instead.