72 findings, 0 of them High: why a finding count is not a score, and the three headers behind most of mine.

I ran my own website release review on two sites I operate, and the first number out of it was 72 findings. If you have spent any time around these reports you already reacted to that number. I want to explain why the reaction is wrong, and then get to the part that is actually useful: three response headers produced almost all of those 72, and each has a one-line fix.

What 72 counts, and what it does not

The report is a grid: one row per check per URL. My first run covered 24 URLs and produced 957 rows — 72 needing action, 865 confirmed OK, 20 not checkable from where I was sitting. So 72 is not a mark out of 100. It is not even 72 problems. It is 72 cells in a spreadsheet that failed, and because some checks are origin-wide rather than page-specific, one absent header can fail on all 24 pages and contribute 24 cells.

Mine did exactly that. Three checks failed on 24 of 24 pages, which is why the count looks lumpy: 3 × 24 = 72. Three real gaps, reported 72 times. Any tool that hands you a single score would have told you "72 problems!" about a site whose actual to-do list is three lines of configuration — and would have been technically within its own rules doing it. That is the reason my report states, in its own words: "These are plain counts of individual findings. They are not a quality score… not comparable to any other tool or site."

And the severity split matters more than the total: 0 High, 72 Medium, 0 Info in that run. Nothing in the 72 was going to break the site tonight. The second site — 12 URLs, 502 rows — is where the performance-style items landed, graded Info rather than Medium: a sampled page asset served with Cache-Control: max-age=0, so the stylesheet is re-fetched on every visit, and one render-blocking stylesheet in the <head> that holds first paint until it arrives.

The three headers, and what each absence costs a visitor

1. Strict-Transport-Security — absent. Without HSTS, a visitor on untrusted Wi-Fi can be quietly pushed back to plain HTTP by anyone between them and the server, and their session cookie goes out in the clear. The fix is one line at the CDN or server: Strict-Transport-Security: max-age=31536000; includeSubDomains. The caveat worth knowing before you paste it: HSTS is sticky in the browser, so once you publish a long max-age you are committed to having a valid certificate on every subdomain you covered for that whole window. Ship a short max-age first if you are not sure.

2. Content-Security-Policy — absent, in neither enforcing nor Report-Only form. CSP is the main brake on an injected or third-party script doing whatever it likes inside your pages — the script tag your analytics vendor added, the paste from a blog post six months ago. The naive fix breaks the site, so do it in the order that does not: ship Content-Security-Policy-Report-Only: default-src 'self', read what your own pages would have violated, add exactly those exceptions, then flip to enforcing.

3. Frame-embedding protection — both X-Frame-Options and CSP: frame-ancestors absent. A third-party page can drop your site into an invisible frame and let someone click on your button while thinking they are clicking theirs. Fix: X-Frame-Options: SAMEORIGIN, or frame-ancestors 'self' inside your CSP.

What passed, so "OK" has a shape: X-Content-Type-Options: nosniff, Referrer-Policy: strict-origin-when-cross-origin, cookies flagged Secure; HttpOnly; SameSite=Lax, a Server header publishing no version, TLS 1.3 negotiated, certificate valid with 87 days left. The certificate countdown is the only one on that list that will ever become an emergency on its own — every visitor gets a full-screen browser error, not a soft warning — so the cheap insurance is an alarm under 14 days.

If you are buying a review, ask two questions

  1. "Is your number a count or a score?" If the answer is a grade, ask what it is normalised against — a grid of per-URL checks has no denominator, and a tool that invents one is guessing.
  2. "Does the report separate what failed, what passed, and what it could not check?" Mine separates all four — confirmed defect, contextual review prompt, passing check, not checked — because a row that silently means "we did not look" is worse than an empty one.

One boundary I will repeat rather than let you discover it: this pass reads response headers and returned markup. It does not execute JavaScript, so it describes what the server returned, not what your browser eventually paints. Twenty rows in my own run are marked not checked for exactly that reason. No number here is estimated, modelled or inferred — if a row says not checked, it is because it could not be read from where I stood.

The whole fixed-scope version of this — up to 25 URLs of one live site, delivered as a written evidence report with per-URL findings, observed values, why each matters and what to change — is USD 99: https://svc.openyuri.com/pricing . Implementation, if you would rather I did the edits, is quoted separately per milestone.