- Jun 2, 2026
- 6 min read
What My Website Audit Checks and How the Score Is Computed
A score is only useful if you can explain where every point comes from. When someone asks “why did my site get 64?”, I want to answer with a list, not a shrug. So the audit is deliberately boring: a handful of well-known checks, fixed weights, and no machine-learning magic in between.
This post lists every check, how each category gets its 0–100 score, and how the four categories become the overall grade. The previous post covers the architecture that runs them.
The checks at a glance
| Category | Tool / script | What it looks at | Weight |
|---|---|---|---|
| Performance | Lighthouse (lighthouse-audit.sh) | Lighthouse performance score, mobile by default; LCP, CLS and JS weight extracted for the report | 40 |
| Technical SEO | seo-audit.sh → seo-live-audit.mjs | robots.txt, sitemap.xml, homepage metadata | 20 |
| Security | security-audit.sh + tls-check.mjs | HTTPS, certificate, redirect, TLS version, 5 security headers | 20 |
| Links | linkcheck-audit.sh (linkchecker) | Broken links up to 3 levels deep | 20 (when enabled) |
| Latency | pinghttp-audit.sh | Ping and HTTP response times, TLS handshake time | not scored |
Performance: Lighthouse, mobile first
Lighthouse runs in headless Chromium inside the container. By default it
runs the mobile profile only (LH_FORM_FACTOR=mobile): Google ranks on
mobile and most real traffic is mobile, so that’s the number that matters.
desktop or both are one environment variable away.
The category score is Lighthouse’s own performance score, read with jq:
PERF_SCORE=$(jq -r '.categories.performance.score * 100 | floor' "$LATEST_LH")
The enhancement layer also pulls a few raw metrics out of the same JSON — Largest Contentful Paint, Cumulative Layout Shift and the JavaScript weight — because “LCP is 3.3 s” means more to a site owner than “performance 71”.
Technical SEO: robots, sitemap, homepage
The SEO script is a small Node program that fetches three things and scores each:
| Part | Share of SEO score | Points for |
|---|---|---|
robots.txt | 20 % | Served with HTTP 200 (less if it redirects), declares a Sitemap: line |
sitemap.xml | 20 % | Served with HTTP 200, valid, contains <loc> URLs |
| Homepage | 60 % | <html lang>, viewport, charset, title (30–65 chars), meta description (50–160), canonical matching the URL, exactly one <h1>, Open Graph and Twitter tags, JSON-LD, image alt texts |
overall: clamp((robots.score * 0.2) + (sitemap.score * 0.2) + (page.score * 0.6)),
Every miss also becomes a finding with a severity, so the report can say what is wrong, not just how much.
Security: transport first, headers second
Security is 100 points split in two:
| Part | Points | Check |
|---|---|---|
| Valid HTTPS | 30 | TLS handshake succeeds and the certificate is trusted for the hostname |
| http → https | 15 | Plain http:// redirects to https:// (or nothing listens on port 80) |
| Certificate expiry | 10 | More than 14 days left |
| TLS version | 5 | TLS 1.2 or newer |
| Security headers | 40 | Content-Security-Policy (40 % of this part), HSTS (20 %), X-Frame-Options (15 %), X-Content-Type-Options (15 %), Referrer-Policy (10 %) |
The transport part matters more because it’s what visitors actually see: an invalid or expired certificate is a browser warning, a missing CSP header is not. How the HTTPS checks work in Node has its own post later in the series.
Links: a sample, and off by default
The link check uses linkchecker with --recursion-level=3: the homepage,
the pages it links to, and two more hops. It’s capped at 180 seconds by its
own script and, in practice, by the 120-second limit on the whole audit. The
score is simple:
LINK_SCORE=$(( 100 - (ERRORS_COUNT * 10) ))
[ "$LINK_SCORE" -lt 0 ] && LINK_SCORE=0
If the check is cut short, a partial result scores from 70 down instead of 100, so an incomplete run never looks perfect.
It’s off by default. A link check is the slowest phase and the heaviest on the audited server — dozens or hundreds of requests to someone else’s site, triggered by whoever typed the URL. Visitors can switch it on in the advanced options when they want it.
Latency: measured, not scored
pinghttp-audit.sh sends a few pings and HTTP requests and records DNS, TCP,
TLS handshake and time-to-first-byte. It’s useful context in the full report,
but it depends too much on where my server is relative to theirs to be fair
as a score.
From four categories to one grade
flowchart LR
P["Performance<br/>Lighthouse mobile<br/>weight 40"] --> W
S["Technical SEO<br/>robots 20% · sitemap 20% · homepage 60%<br/>weight 20"] --> W
L["Links<br/>100 − 10 per broken link<br/>weight 20, or 0 if skipped"] --> W
X["Security<br/>HTTPS 60 + headers 40<br/>weight 20"] --> W
W["Weighted average<br/>sum(score × weight) / sum(weights)"] --> G["Overall grade<br/>0–100"]
The master script multiplies each category by its weight and divides by the total weight of the categories that actually ran:
TOTAL_WEIGHT=$(( W_LH + W_SEO + W_LINKS + W_SEC ))
RAW_SUM=$(echo "($PERF_SCORE * $W_LH) + ($SEO_SCORE * $W_SEO) + ($LINK_SCORE * $W_LINKS) + ($SEC_SCORE * $W_SEC)" | bc -l)
OVERALL_SCORE=$(echo "$RAW_SUM / $TOTAL_WEIGHT" | bc -l | awk '{printf "%.0f", $1}')
The important detail: a skipped category gets weight 0. With the link check off, the overall grade is built from performance, SEO and security only — a skipped check never drags the score down as if it had failed.
Honest limits
I’d rather say these out loud than have a client discover them:
- SEO looks at the homepage only. It catches the basics that matter most, but it isn’t a site-wide crawl.
- The link check is a sample, three levels deep and time-boxed — not every page of a large site.
- Security is what a visitor’s browser can see: transport and response headers. It isn’t a penetration test and doesn’t try to be.
- Performance is one Lighthouse run, so it moves a few points between runs. Big differences are signal; small ones are noise.
Takeaways
- Pick well-known tools and fixed weights, so every point is explainable.
- Score what the visitor experiences (certificates, speed) above what only a scanner notices.
- Give skipped checks zero weight instead of a zero score.
- Write the limits down; they make the rest of the report more believable.
Try it on your own site at webaudit.jalcocertech.com.