pickuma.
Dev Knowledge

Zero Social Referrers in 30 Days Across 35 Browser Games

Two causes: referrer stripping made the loop unmeasurable, and an await before navigator.share() failed silently on iOS Safari.

Updated 2026-08-21 6 min read

Thirty days, 35 browser games, one share button per game, and the referrer breakdown in Cloudflare Web Analytics contained no social host at all. Not a low number. Nothing. The button was not broken in any way that showed up: it opened the OS share sheet on the machine we developed on, it wrote to the clipboard, and the console stayed clean.

That gap — the button works, the loop does not — turned out to have two unrelated causes stacked on top of each other. One is a measurement problem that makes “zero referrers” much less informative than it looks. The other is a real bug that silenced the button on the platform where most of the traffic is. We fixed both. We have not yet measured whether the fix produces shares, and the last section says so plainly.

What “zero social referrers” actually measured

The Referer header is a weak instrument for exactly this question. X wraps outbound links in t.co and its mobile apps commonly send no referrer at all. In-app webviews — Instagram, TikTok, most embedded browsers — frequently omit it. Every share that lands in a DM, an iMessage thread, a WhatsApp group, or a Discord channel arrives as direct traffic. Those are the places a game link actually travels.

So an empty social row is consistent with two very different worlds:

  1. Nobody shared anything.
  2. People shared, and the referrer was stripped before it reached us.

We could not tell those apart, which means the 30-day figure that started this work was never evidence that the share loop failed. It was evidence that we had no way to observe it. If you are looking at a similar dashboard, resolve that first, because the two worlds call for opposite responses.

The only fix is to put an identifier inside the payload you hand to the user, not to rely on what the receiving platform decides to forward. We append a short suffix to the shared URL. That creates a real tension the rest of this article has to work around: the thing that makes a share measurable is a link, and a link is the part platforms downrank and users skip.

Why an emoji grid is a different object than a score sentence

Our original share text was a templated sentence — the game name, the score, an exclamation mark, and a URL. Compare that to the format everyone started copying after Wordle’s grid spread through January 2022: six lines of colored squares and no link.

The grid is not better copy. It is a different kind of object, and four properties do the work:

  • It carries its own rendering. The squares are plain Unicode (U+1F7E9 and friends, added in Emoji 12.0 in 2019) and ship in the system fonts on iOS, Android, Windows and macOS. Pasted into any client, it renders. No Open Graph fetch, no crawler, no card, no dependency on the platform choosing to unfurl your domain.
  • It is spoiler-safe. It shows the shape of an attempt without the answer, so posting it costs the sender nothing socially.
  • It is comparable. Every player who posts one that day is describing the same puzzle on the same axis. That turns a post into a thread — the reply is “4/6 here,” not silence.
  • It is reproducible. The format is identical across senders, so it reads as a convention rather than as an ad.

“I scored 4,820 in Tile Cascade!” fails all four. It renders as a link card or as nothing. It has no shared reference frame — 4,820 against what? There is no reply available except congratulations. And a bare number next to a URL is the exact shape of promotional copy, so it gets read as promotional copy.

The distinction that mattered for us: a grid encodes a comparable state, a sentence encodes a claim. Claims need the reader to trust the sender. Comparisons only need a shared axis.

Which exposes the part that is not a formatting change at all. Our 35 games were free-play with random seeds. There is no shared axis, so there is nothing for a grid to encode — any grid we generated would have been decoration on a claim. Shipping the format required shipping a daily seed first: one deterministic instance per game per UTC day, derived from the date so every player gets the same board. That is an architectural change to the game loop, not a change to a share button.

The bug that silenced the button on iOS Safari

Our handler minted the share identifier server-side. The sequence was: click, await fetch('/api/share-id'), build the string, call navigator.share(). On desktop Chrome this worked, which is why it shipped.

On iOS Safari it did not. The await cost us the transient activation, share() rejected, the fallback clipboard write rejected for the same reason, and the catch block was empty. No sheet, no toast, no console error a user would ever see. Mobile is where a game gets shared, so the loop was effectively dead on the platform that mattered while looking healthy in development.

The fix has three parts, and none of them are clever:

  • Derive the share identifier client-side from the daily seed and the result. No network call in the handler, so nothing to await.
  • Feature-detect with navigator.canShare?.({ text }) before calling, and fall back deliberately rather than by exception. Desktop Firefox has no navigator.share at all — check current support rather than trusting this sentence.
  • Make the fallback visible: render the text in a selectable block with an explicit copied state, instead of a silent clipboard write that can fail without telling anyone.

What we changed, and what we have not measured

Shipped: a UTC daily seed per game, a grid-style result block built from the day’s board, share text that leads with the grid and puts the URL on its own last line, a synchronous share handler, and a visible copy fallback.

Not measured: whether any of it produces referrers. This article is the diagnosis and the change, not the result. We also did not test Android Chrome Custom Tabs, did not verify how the fallback link renders as a Bluesky card, and have not checked whether the daily seed reduces total sessions — capping a free-play game at one board a day is a real cost, and it is plausible the trade is negative.

If you cannot ship a shared daily instance, do not copy the grid. Ship a decent per-game og:image and accept ordinary link-card sharing; a grid with no common axis is noise with extra steps. The condition that flips it is exactly that axis — the moment every player is solving the same thing on the same day, the format starts doing work that copy cannot.

And given how much of this came down to not being able to observe our own traffic: a channel you own answers the question directly. Referrers are optional; a subscriber list is not.

beehiiv

Newsletter platform with per-post open and click data, so distribution you own is measurable without depending on referrer headers that social apps strip.

Free tier available; paid plans add analytics and automation

Try beehiiv

Affiliate link · We earn a commission at no cost to you.

FAQ

Does an emoji grid work without a daily puzzle?
Not in our reading of why it spreads. The grid works because every player that day describes the same board, which gives readers a comparison axis and gives the sender something to reply to. With random seeds there is no axis, and the grid degrades into a decorated score claim.
Why did the share button fail on iOS Safari but not desktop Chrome?
navigator.share() requires transient user activation. Chromium keeps that activation alive for roughly five seconds, so an awaited fetch inside the click handler usually survives. Safari drops it as soon as the handler yields, so share() rejected with NotAllowedError and the clipboard fallback failed for the same reason.
If referrers are unreliable, how do you measure a share loop at all?
Put an identifier in the payload you generate rather than relying on the receiving platform. A short suffix on the shared URL survives DMs and in-app browsers that strip Referer. It does mean keeping a link in text that would spread better without one, which is a trade rather than a solution.

Tools used in this review

Some links above are affiliate links. We may earn a commission if you sign up. See our disclosure for details.

Related reading

See all Dev Knowledge articles →

Get the best tools, weekly

One email every Friday. No spam, unsubscribe anytime.