We use cookies for analytics and advertising to understand traffic and improve EarthGuessr. You can accept or reject — essential cookies always stay on. Privacy & cookies

All posts
Behind the ScenesFebruary 7, 20266 min readEarthGuessr Team

Anti-Cheat: How We Keep the Leaderboard Fair

Server-side scoring, coordinate obfuscation, zoom restrictions, and a row-level security model that hides the answer until the round is over — how we make cheating expensive.

Anti-Cheat: How We Keep the Leaderboard Fair

A geography game lives and dies on its leaderboard. The whole appeal of climbing one is the assumption that the people above you actually earned their score. If the top entries are obviously fake — five 5,000s, completed in seven seconds — the leaderboard becomes worthless and the game loses its point. Anti-cheat is therefore not a feature we can add later. It has to be in the foundation.

This post walks through the specific measures we use to make cheating in EarthGuessr expensive enough that almost nobody bothers. The threat model is not a state actor with a custom client — those are unstoppable on any web game — but a smart, motivated player with browser dev tools open. That covers the realistic worst case.

Threat 1: reading the answer from the network response

The most obvious attack on a location-guessing game is to open the network tab, watch the response that delivers the satellite frame, read the latitude and longitude out of the payload, and submit the exact answer. We saw this attempted very early on, and the fix had to be foundational.

Our solution is coordinate obfuscation. When the server sends the location for a round, the coordinates in the response are offset by a small random amount — roughly a few hundred metres in latitude and longitude. This is enough offset to ruin a cheater's "submit the response coordinates" attack while being small enough that the satellite imagery still loads at the right place (the map provider does not care about a few hundred metres at zoom level 14).

The real coordinates never leave the server during gameplay. Scoring is done server-side against the actual coordinates stored in the database, not against the obfuscated coordinates sent to the client. Even a player who reads the network response perfectly will submit a guess that is a few hundred metres off the true location. That is not a free 5,000.

Threat 2: querying the database directly

Supabase, which we use as our backend, exposes a fairly broad data access layer to clients by default. A sufficiently determined player could in principle write a query to read the locations table directly, get a list of every location with its exact coordinates, and never need to guess again. We had to make sure that was not possible.

The fix is row-level security policies. The locations table has no SELECT policy for anonymous or authenticated users — meaning regular client queries cannot read it. Only server-side functions running with elevated privileges can access the table. Those functions are the API surface for starting a game and submitting a guess. A player with full access to the JavaScript console cannot enumerate the locations table because the database refuses to return any rows.

The game_rounds table — which stores the per-round answers and submissions — is protected by a similar policy. A player can only read rounds they have already submitted, and only for their own sessions. They cannot read other players' rounds, and they cannot read the actual answer for a round that is still in progress.

Threat 3: faking scores in submission

Many web games make the mistake of letting the client compute a score and submit it. This is always wrong, because the client is hostile — anyone can modify the JavaScript to submit any score they want. We never trusted the client to compute scores.

The submission API takes a guess (latitude and longitude) and nothing else. The server then computes the distance between the guess and the actual location using a standard haversine formula, applies our scoring curve, and writes the score to the database. The client can never directly write to the score column. It can only submit a guess and accept whatever score the server returns.

This means the worst a cheating player can do is submit a perfect guess (which still requires knowing the right answer, which we have already made hard to obtain). They cannot inflate their score, cannot fake distances, cannot mark a round as complete without submitting.

Threat 4: replaying or double-submitting

A subtler attack is to submit the same round multiple times — guess, see the answer in the result screen, then re-submit with the now-known correct coordinates. We block this at the database level. Each round has a submitted_at timestamp that gets set on first submission. Any subsequent submission for the same round is rejected by the server with a "round already submitted" error. There is no client-side flag to bypass.

A similar protection applies to the daily challenge: once you submit a guess, that round is locked, and once you complete all five rounds the entire daily attempt is marked finished. The server records one completion per user per calendar day and refuses any further attempts, so you cannot replay the daily after seeing the answers.

Threat 5: zooming out to see the wider context

One of the most effective natural "cheats" is just zooming out of the satellite view far enough to see a city, a coastline, or some other distinctive feature that gives away the location. This is not cheating in any meaningful sense — it is just defeating the game by making it easy. We restrict it not because it is malicious but because the game stops being interesting if you can do it.

The map enforces minimum and maximum zoom levels per game mode. In the strictest mode (no zoom, no move) the map is locked at the starting zoom and you cannot pan. In the standard "move" mode you can pan freely but the minimum zoom is clamped so you cannot pull back to a city-scale view. We even hook into the map's zoom event to forcibly snap the zoom back if it drifts outside the allowed range — which catches the case of a player using dev tools to programmatically set the zoom.

What we do not bother with

A few things we deliberately do not try to defend against, because the cost of defending exceeds the cost of the attack.

  • Players who take a screenshot of the satellite frame and run it through Google Reverse Image Search. You can sometimes get the answer this way. We could mangle the imagery to defeat reverse search, but the trade-off in visual fidelity is not worth it for what would only stop a small minority.
  • Players who use multiple devices to play the daily twice. The server enforces one attempt per account, so this requires multiple accounts. The fraud-prevention bar for "stop someone making two Google accounts" is much higher than the value of the daily leaderboard.
  • Players who are extremely good at the game. Some legitimate scores look indistinguishable from cheating — a 4,950 from a single confident click, made by a player who has played five hundred games. We do not flag these. The whole point of the leaderboard is to celebrate them.

The principle

The general principle behind every measure above is the same: the client is never trusted. The server is the only authority. The client's job is to render whatever the server sends and to forward player input. Everything else — scoring, location data, anti-replay — happens on the server, where the player cannot touch it.

This is the only model that works for a competitive game on the open web. We had to design for it from the start because retrofitting anti-cheat onto a client-trusting architecture is enormously painful. The good news is that once the foundation is right, individual defences become straightforward — a small obfuscation here, a row-level policy there, and the leaderboard stays honest.

More in Behind the Scenes

Related reading

Ready to explore?

See the world from above and test your geography skills on a 3D globe.