---
title: "The Signup Spike Wasn't the Fix, It Was the Season"
canonical: https://dxdev.com/blog/2026-09-03_signup-spike-was-seasonal-not-the-seo-fix/
datePublished: 2026-09-03
---
The alert said signups were up 59% week over week. Unusual enough against the prior two years' seasonal baseline for the same week that it looked like a real signal, and the obvious suspect was the SEO work: we'd just added structured data to the individual FAQ article pages and shipped a new glossary page with its own signup link. Something shipped, spike right after. Easy story.

First move was to check whether the glossary link was actually driving it. It has its own tracking, so I pulled the conversion numbers for `sign_up` events attributed to that link against total tracked signups for the trailing three weeks. It came back at 0.49%, under one signup in two hundred. Not the cause, not close. That ruled out the fix I most wanted to be true, and it's the point where the investigation actually should have widened, but the SEO angle still felt live because the timing lined up, so I kept pulling on it before turning to signup composition directly.

What broke the SEO theory for good wasn't a second attribution query, it was looking at where the referral data itself was coming from. One tracking field is only populated from an explicit `?src=` or `?source=` query param, and today only paid ads set that param. A second field is a long-lived cookie, but it's only written if a visitor's very first pageview ever on the site had an external referrer. Direct visits, bookmarks, and anyone who clicked through mid-visit (glossary to signup, help topic to signup) never populate it. The same capture logic was copy-pasted near-identically across four separate site templates.

Once I ran the numbers, 55% of that week's signups had both fields blank. Not attributed to search, not attributed to anything. More than half the funnel was invisible to the tracking, all the time, not just during the spike. The 59% jump wasn't SEO-driven organic acquisition disguised as blank rows. It was ordinary seasonal growth arriving on top of a measurement system that was already blind to most of its own traffic.

That gap became the ticket that tracked this fix, filed as a hotfix and cut from the production branch into its own working branch. The fix:

- Accept a UTM source param as an alias for the legacy param, so tagged campaigns stop getting dropped.
- Capture UTM medium, campaign, term, and content into a new long-lived cookie, deliberately not wired to any database write yet.
- When the referrer cookie exists (confirmed external referrer) but the source field is blank, derive a coarse source label from the referrer's domain server-side, instead of leaving it hard-blank.
- Tag internal links with their own source going forward, starting with the glossary link.

The one bug the review actually caught was in that last piece. The derivation logic needed to fire only when the referrer was confirmed external, using the existing cookie as the gate, not on every blank source field. The first pass had that gate loose enough that an internal click-through, glossary to signup with no tag, was at risk of getting labeled as an external referral instead of left alone. Getting that gate right mattered more than the rest of the ticket combined, because the failure mode wasn't a missing feature, it was actively mislabeling rows inside the 55% that had no attribution at all, turning an honest blank into a false external referral.

Before it touched production I ran a review pass with a second agent, explicitly told not to trust the implementing agent's summary: read the actual diff, not the description of it, confirm the external-referrer gate holds on every changed path, confirm the legacy param behavior is unchanged, confirm the new UTM cookie really is cookie-only with no database write, and reproduce the claimed test scenarios independently rather than re-running the existing harness blindly. That pass is what confirmed the gate fix actually closed the mislabeling path across all four files, not just the one it was first caught in.

It shipped as a hotfix, verified live on production. A teammate then found the same gap one layer up: the structured data fix credited for part of the spike story had gone out on the individual FAQ article pages, but not on the FAQ index pages that link to them. That went out as a second hotfix on the same ticket. Two real fixes, neither one the explanation for the number that started the investigation. The 59% was just the season. The blank referral data was the thing worth finding, and it had been sitting there under working-looking numbers the whole time.
