# The http redirect a static host does not add for you

> A host that serves https does not always redirect plain http to it. What a plain-http request really does, and the one-line fix on four common hosts.

Source: https://plantroomlabs.com/notes/http-to-https-on-a-static-host/  
Published: 2026-10-02 (2 October 2026) · Plantroom Labs  
Topics: https, Static hosting, Deployment

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.

## 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.

## Where this is written down

- [RFC 9110: HTTP Semantics](https://www.rfc-editor.org/info/rfc9110/) — 301 and 308 are both permanent, and the difference between them is whether a client may change the request method, which is why 308 exists at all
- [RFC 6797: HTTP Strict Transport Security](https://www.rfc-editor.org/info/rfc6797/) — the header must be ignored when it arrives over plain http, and max-age plus includeSubDomains is a commitment with no fast way back
- [RFC 8555: Automatic Certificate Management Environment (ACME)](https://www.rfc-editor.org/info/rfc8555/) — http-01 validation is an ordinary http request for a file under a well-known path, which is why a blanket port-80 redirect has to leave that path alone
