About this audit

Pikkola, tool No. 19

One request from the edge, every hop kept, six headers graded, and the configuration to close the gaps.

Why a server has to look

When a page loads a stylesheet, a font or an API from another origin, the browser hands JavaScript a short list of response headers and nothing else. HSTS, Content-Security-Policy, X-Frame-Options and the rest are not on that list. The redirect chain is not available either: the browser follows it before your code runs, so all you can observe is where it ended.

That leaves two options for anyone who wants to see their own production headers: the command line, or a page that asks a server to look on their behalf. This is the second one. A Cloudflare Worker makes one request to the address you give, keeps what came back, and returns it as JSON to the page.

Following the chain

Redirects are followed by hand, not by the fetch library, so each one is a separate request with its own status code, its own Location and its own duration. Up to six hops. A redirect is followed only if it stays on http or https and its host passes the private address check.

The chain is printed in order, including the ones that make a site look tidy in a browser and untidy in a report: a hop from HTTPS back to HTTP, a redirect to a different host, a loop that never settles. If the chain ends without a document, the result says so and shows the last hop instead of inventing a grade for a page that was never served.

The private address gate

A server that fetches any address it is given is a way into networks the visitor cannot reach. So every host is resolved through DNS-over-HTTPS before the request, and the answer is refused if it lands in loopback, link local, the private ranges, carrier grade NAT, multicast, or a cloud metadata address. Host names that only exist inside a network, such as a single label name, a .local name or a .internal name, are refused before resolution. Redirect targets go through the same gate.

This is a real limit worth stating plainly: the address is resolved just before the request, and a DNS answer can change between those two moments. The gate raises the cost of an attack considerably and does not remove it. That is why the tool opens only the ordinary web ports and never returns the body of the page it fetched.

What is graded, and what that means

Eight checks carry weights: transport 15, HSTS 20, Content-Security-Policy 25, frame protection 10, content type 10, referrer 10, permissions 5 and opener policy 5. A pass earns the full weight, a warning half, a failure nothing. Two caps apply: plain HTTP stops at C, and a missing content policy stops at A.

The letter is a summary of those responses, not a judgement about the application behind them. A site can score A+ and still have a broken login, an outdated dependency or an injection in a search box. A site can score D and be a static page that holds nothing worth taking. The checklist is worth reading for the items that failed, which is where the actual work is.

What it does not do

It does not run any of the page: no scripts, no images, no stylesheets. It does not test TLS versions, certificate chains or cipher suites. It does not look for known CVEs, open ports, cookies without flags, or authentication behaviour. It does not crawl: one address, one document, and the head of that document for the meta tags.

It also cannot audit a site that only exists on a private network or on your own machine, because the request comes from Cloudflare's network rather than from your connection.

And it refuses to audit its own address. A worker cannot fetch itself through its own route: the request would go straight to the origin, so the answer would be missing the headers this worker adds. Rather than print a grade no visitor ever sees, it says so.

If something is wrong

Write to hello@pikkola.com with the address, the grade and what you expected. Do not send credentials for the site you are auditing.

Back to the audit