Each study below states its dates, its method and its limits. Where a before-and-after measurement exists, the same test was used both times; where none exists, the study reports counts and says so. We don't publish traffic, ranking or revenue figures we can't tie to a specific, dated test.
Technical SEO · Window Film & Tinting
NorthGuard Window Film: 19 routes, one byte-identical HTML shell
A site-wide rendering bug meant every URL returned byte-identical HTML. Previously reported technical SEO audit scores were 35/100 on July 9, 2026 and 98/100 on August 10, 2026. The audit files and tool version are not archived for republication. This was not a single-variable result: the site was also rebuilt on Next.js and its blog reinstated. Read the full study
Baseline · Jul 9, 2026
35/100
Technical SEO audit score
Re-audit · Aug 10, 2026
98/100
Technical SEO audit score
Scope
19
Routes restored to distinct HTML
The finding
Our 2026-07-09 audit hashed the raw HTML returned for every route on the site. All 19 URLs produced an identical MD5. The site was shipping a client-rendered shell with no server-side rendering and no prerendering, so the HTML a crawler received never varied by route — same title, same canonical, same body, on every page.
Every route initially returned the same app-shell HTML, forcing crawlers that rely on initial HTML — and Google before its rendering phase completes — to depend on JavaScript execution just to discover each route's unique content. Static prerendering moved the page-specific title, canonical, and body content into the initial response, removing that dependency entirely.
The fix
We moved the site onto Next.js prerendering so that each route emits its own fully-formed HTML document at build time. Every URL now ships a distinct <title>, a self-referencing <link rel="canonical">, and its own body content in the initial response — before any JavaScript runs.
The verification
We re-ran the identical audit on 2026-08-10 against the live production site. The previously reported technical SEO score was 35/100 on 2026-07-09 and 98/100 on 2026-08-10. The duplicate-HTML check that failed on the 19 audited URLs now passes on the same 19 URLs, confirmed by fetching each URL and diffing the raw response — not by trusting a dashboard.
Why this is the Gap Analysis pattern
The score measures the crawlability and indexability of the HTML a crawler receives. Between the two runs the site was also rebuilt on Next.js and its blog was reinstated, so we don't present this as a single-variable experiment: the check that failed was the duplicate-HTML check, and that is the check that now passes. That's the whole thesis: find what's structurally broken before you pay to drive traffic into it.
Technical SEO · Window Film & Tinting
GlassGuard Window Film: rebuilding around what actually ships in production
Production was serving a static build while the site's own server-rendering code never ran in that environment. We rebuilt the per-route head — title, canonical, meta, structured data — on a prerendering pipeline so every URL is individually addressable to search engines, and remapped every legacy URL so old links and old search-engine records land in the right place. Read the full study
Sitemap
54
Indexable URLs
Legacy URLs remapped
6
Old GoDaddy service-area pages redirected to their new locations
The finding
Production traffic was served as a static deployment — a file server reading pre-built HTML — while the site's own server-rendering code assumed a live server that never actually ran in production. Any logic that depended on that server — including server-side redirects — was silently doing nothing in production.
The fix
We rebuilt the site on a Vite + prerender pipeline so every route ships its own distinct <title>, canonical link, meta description, and structured data in the initial HTML response — fixing the identical-shell problem that made every URL indistinguishable to a crawler reading only the initial response. The page body still renders client-side after JavaScript runs; that part of the site was not part of this fix. Six legacy URLs from the old GoDaddy site were mapped to their new locations with static redirect stubs so old links and search-engine records land correctly instead of 404ing.
The verification
Per-route head metadata (title, canonical, description, structured data) confirmed live and correctly varying by URL, Sep 10 2026.
Analytics Instrumentation · Schkovl's own site
schkovl.com: a month of consent-gated analytics that recorded nothing
Google Analytics 4 went live behind a consent banner on 2026-08-26 and recorded nothing for a month, even for visitors who accepted. The site's gtag stub queued plain arrays; gtag.js reads only Arguments objects, so every queued command was ignored. The cause was two lines; the fix changed five and added a regression test. Read the full study
Requests · Sep 24, 2026
0
GA4 collect requests after a visitor accepted analytics (headless reproduction)
Event types · Sep 26, 2026
3
GA4 Realtime event types recorded for the first consented test visit: first_visit, session_start, page_view
Change
5 lines
Lines changed in one source file (3 added, 2 removed), plus a regression test
The finding
We installed GA4 on schkovl.com on 2026-08-26 behind a Consent Mode v2 banner: nothing loads until a visitor clicks Accept. For the next month the property showed "No data received". By 2026-09-10 we had ruled out a Content Security Policy block, then wrongly attributed the missing requests to the test browser's own extensions. A code audit on 2026-09-24 found the cause: the site's gtag stub pushed a plain array onto the dataLayer. gtag.js reads only Arguments objects from that queue, so every consent, config and page_view command was ignored — even after Accept.
The fix
The stub now pushes its Arguments object instead of a rest-parameter array: three lines added, two removed, in one file. A regression test asserts that each queued entry is an Arguments object, so the same mistake fails the test suite if it comes back.
The verification
Before the fix, a headless reproduction on 2026-09-24 sent zero /g/collect requests after accepting analytics. After the fix deployed on 2026-09-26, the queued dataLayer entries read [object Arguments], a page_view request went to /g/collect, and GA4 Realtime recorded first_visit, session_start and page_view for the test visit. On 2026-10-01 a fresh browser session sent no collect request before consent and two after clicking Accept.
Why we publish our own mistakes
A month with no data is exactly the kind of gap the Gap Analysis exists to find. The defect was invisible from the dashboard, survived a plausible wrong explanation, and fell to a code read that checked what the browser actually queued.
Clients
Clients named with their approval
Each entry is a client who agreed to be named, with a live link and a one-line scope. Results appear only in a study above, or not at all. No logos, no quotes.