Headers · read through a guarded relay

What a site says
before the page arrives.

Every answer a web server gives opens with a block of headers that the browser reads and the visitor never sees: go here instead, keep this for an hour, run scripts only from these places, never show this page inside another site’s frame, remember this cookie. Type an address and this reads that block — every hop of the redirect chain, every header on every hop — and takes the security headers apart one line at a time.

It is a checklist, not a grade. A header that is missing is said to be missing, with what that leaves open; one that is present is read directive by directive. The fetch is made by a relay on this site that reads the public internet and nothing else — private networks, loopback, cloud metadata addresses, IP literals and odd ports are refused by rule, and the method below lists every refusal.

HEAD first, GET only if HEAD is refused · five redirects at most, each one checked again · ten seconds and 64 KB · cookie values never shown · nothing logged

Read

One address, every hop.

A bare name is read as https://; type http:// to see whether the plain door sends you on. The relay asks with HEAD — the request for headers alone — and asks again with GET only if the server refuses HEAD. Redirects are followed by hand, five at most, and each new address is checked again before it is fetched.

The answer

waiting for an address

Enter reads it too. The address goes to this site’s relay in the body of a request, is not logged; the answer is kept two minutes, locked with the address as its key, then deleted on the relay’s next call.

Nothing read yet. The answer lands here: the final address and its status, then the chain, hop by hop.

The checklist

Each security header, line by line.

Four words and no score: in place, worth a look, not sent, and for information. A letter grade would have to decide how much a missing policy matters to a site this page knows nothing about; the list says what each header does, what the value sent means, and what its absence leaves open — and leaves the weighing to you.

Security headers

Read an address above and the checklist fills in here.

Every header

Every header of the final answer, in the order the server sent them, each with a line on what it is for. Earlier hops fold away beneath it.

Method

What is fetched, what is refused, what is kept.

The fetch

Your browser cannot read another site’s headers itself — most of them are hidden from script by design — so the page asks a relay on this site, headers/headers-relay.php, and the relay asks the address. The site you name sees labs.llc’s server and the user agent labs.llc-headers/1.0, never you. It is asked with HEAD; if HEAD fails or is answered with an error, it is asked again with a GET whose body is dropped after 64 KB, and the hop says so. Redirects (301, 302, 303, 307, 308) are followed by hand, five at most. Certificates must verify: a bad one is reported, never waved through.

The refusals

This is the first page on the site that fetches an address a visitor types, so it refuses by rule, before anything is sent:

  • anything but http:// and https:// — file:, ftp:, gopher: and the rest;
  • any port but 80 for http and 443 for https;
  • a user name or password inside the address;
  • an IP address in any spelling — 127.0.0.1, 2130706433, 0x7f000001, 0177.0.0.1, [::1] — and the special-use names localhost, .local, .internal, .test, .example, .invalid, .onion;
  • a name any of whose DNS answers, IPv4 or IPv6, is not public: private networks (10/8, 172.16/12, 192.168/16, fc00::/7), loopback (127/8, ::1), link-local (169.254/16, where cloud metadata services live, and fe80::/10), carrier-grade NAT, multicast, documentation and reserved space. One bad answer refuses the name;
  • a redirect to any of the above: every hop is parsed, resolved and vetted exactly as the first.

The vetted address is pinned for the connection, so a second DNS answer cannot swap it mid-request, and the address actually connected to is compared with the pin afterwards. Proxy settings from the server’s environment are never used.

The limits

Five seconds to connect and ten for the whole read, redirects included; five redirects; 64 KB of body. Twenty reads a minute and three hundred a day from one address (an IPv6 visitor counts by their /64); past that the relay answers 429 and says how long to wait.

What happens to what you type

The address travels in the body of a POST, so it never sits in a URL a server log keeps, and the relay writes no log of its own. A successful answer is kept for two minutes in a private directory on the server, under a hashed name, so a second look costs nothing. It is stored encrypted with a key made from the address itself, so the file says nothing to anyone who does not already know the address; once its two minutes are up it is deleted on the relay’s next call. The rate counter keeps a hash of your address keyed with a secret that stays on the server, not the address. Cookie values never leave the relay: a Set-Cookie header arrives here as its name and its flags. The last address you read is put in this page’s own address bar (after the #, which browsers never send), so the page can be shared or reloaded; nothing else is stored. What a site sent belongs to that site: it is shown to you, kept those two minutes, and never republished.

The explanations

Everything below the chain is computed in your browser from the header text: Content Security Policy against the W3C’s CSP Level 3, Strict-Transport-Security against the IETF’s RFC 6797, cookies against RFC 6265 and its successor draft, framing, referrer, permissions and the cross-origin policies against the W3C and WHATWG specifications that define them. What headers cannot show: a policy set in a <meta> tag on the page, and headers a server sends only to some visitors — by browser, by cookie, by country. This is one anonymous request from one server, and it says exactly what that request was told.

Questions and corrections: labs@labs.llc.