July 16, 20262 min read

The scanner said my own site was an F

Sentinel graded nyxoralabs.com an F. I’d already hardened it, so only one of us was wrong.

I built Sentinel, a security scanner. The first real thing I pointed it at was nyxoralabs.com, a site I’d already hardened: full CSP, HSTS with preload, frame-ancestors locked down, the whole set. I knew what the answer should be.

It came back F. 28 out of 100. No HSTS, no CSP, no clickjacking protection.

The interesting part is what I did next, because there were two possibilities and only one of them was comfortable. Either my site wasn’t as hardened as I thought, or my scanner was lying to me.

So I checked the site independently. curl -sI https://nyxoralabs.com returned all six headers, exactly as configured. The site was fine. The scanner was wrong.

The bug was one line:

const res = await fetch(url, { redirect: "follow" })

redirect: "follow" does what it says. It follows redirects and hands you the final response. That sounds like what you want, until you realise a scanner needs to know which response it graded. My code fetched, followed some redirects, read headers off whatever came back, and reported as though it had scanned the URL you typed. It had no idea what it actually looked at.

The fix was to stop following blindly and walk the chain by hand: fetch each hop with redirect: "manual", capture every response, and grade the final document response specifically. Record the resolved URL in the report so it says exactly what it graded.

Then something useful fell out. Once I could see every hop, the chain itself became a finding. The plain-http response that redirects you to https carries no HSTS header of its own. Your first request still travels over http, in the clear, before the upgrade happens. HSTS on the final page protects future visits, not that first one. My scanner now reports that, on my own site, correctly.

The bug turned into a feature because I stopped letting the library make a decision I should have been making.

A check that runs on the wrong thing is worse than a check that doesn’t run. The one that doesn’t run is silent. The one that runs on the wrong thing is confident.

Interested in how this maps to your work?

If this kind of thinking fits a problem you're facing, let's talk it through.

Start a conversation EAT (UTC+3) · Typically responds within 24 hours