If you have ever tagged a link with ?utm_source=something and then gone looking for that word in your analytics, you have probably had the same experience I did: it is not there.
I ran into it on a small side project of mine — a one-minute habit tracker with a giveaway: the first 30 people who claim a place get the app and the ebook, one email address each, no card. I spent a week tagging every link I published so I could finally learn which door works. Cloudflare Web Analytics, which sits in front of the domain, does not record query strings. A tagged link and an untagged link are the same URL to it. So every door looked identical, and I had built a week of measurement that could not answer the one question I bought it for.
The fix is small, and I think it generalises to any free-tier offer: stop asking your analytics tool which channel sent the visitor, and make the signup itself carry the tag.
The whole trick, about ten lines
Read the tag off the page's own URL and send it with the write:
/* The channel tag rides in this page's own URL (?src=devto or ?utm_source=devto)
and travels with the signup. It has to be read here: the analytics layer in
front of us does not keep query strings, so a tagged link is otherwise invisible. */
var SRC = (function () {
try {
var q = new URLSearchParams(location.search);
return q.get('src') || q.get('utm_source') || 'direct';
} catch (e) { return 'direct'; }
})();
await fetch('/api/claim', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ email: email, source: SRC })
});
Store source on the row you already write. Then one read endpoint, never linked from the site, returns the count per channel:
{"ok":true,"total":30,"claimed":0,"placesLeft":30,"channels":0,"bySource":[]}
That is the whole system. No cookies, no third-party script, nothing to consent to, and it survives the visitor closing the tab. It also happens to be the only measurement that matters — not "who visited", but "who finished".
The same page asks a visitor to take one of 30 free places, so the counter and the offer live in the same place. One caveat I learned the hard way: a tag read from location.search is lost when your page is served through a redirect that drops the query string, so pick tagged links to the page that actually does the writing.
What the counter says tonight
Measured a few minutes ago, not from memory: 30 of 30 free places still unclaimed, and zero of them attributed to any channel — because there have been zero claims. Across seven articles on this account, 28 reads in total. On the two other brand handles I already had, 311 posts have reached 23 followers. Same product, same writing, and the difference between the doors was never the writing.
So the honest version of the lesson is not "attribution works". It is that a counter tells you which door failed, quickly and without argument — and mine keeps saying bySource: [], which is a real answer: nobody has come through any door yet. What it cannot tell me is why, and no amount of instrumentation will. That part is just: find a door with people standing behind it.
If you are building a free offer: put the channel tag in the signup, store it, and read it back. Then you will at least never argue with yourself about which channel "felt" like it worked.
I build One Minute Magic — one minute a day, photographed, so a missed day cannot quietly end the habit. No account and no card, and the first 30 people get the app and the ebook free, one email address each.
Top comments (0)