Guide
Securing a vibe-coded multiplayer browser game
Browser games built with AI tools often look finished long before they're safe to put online. The usual problems aren't exotic. A leaderboard that accepts any score, a game state the client is trusted to report, or an admin endpoint left over from testing. The rule behind almost every fix is the same: the player's browser is under the player's control. Anything the client says can be changed, so the server has to decide what's true.
Everything below is meant for testing your own game.
Why client-side checks don't count
Players can open DevTools, edit variables, pause JavaScript, replay network requests and write scripts that call your API directly. Minifying or obfuscating code slows them down by minutes, not days. That's fine for a single-player game where cheating only affects the cheater. It isn't fine once there's a shared leaderboard, multiplayer, items with value, or anything tied to payments.
1. Leaderboards and scores
The most common vibe-coded game setup is a Supabase or Firebase table called scores that the client writes to directly. If anyone can insert rows, anyone can post a score of 999,999,999 under any name.
Check it on your own game: play a round with the Network tab open and find the request that saves your score. Right click it, choose Copy as fetch, paste it into the console, change the score and run it. If the new score appears on the board, so will everyone else's fake ones.
Better approaches, from simple to strong:
- No direct inserts from the client. Remove the insert policy on the scores table and submit through a server function (a Supabase Edge Function, Firebase Cloud Function or your API) that writes with server credentials.
- Game sessions. When a round starts, the server creates a session row with a start time and returns its ID. At the end, the client submits the score with that ID. The server checks the session belongs to this player, hasn't been used, and that the score is possible for the elapsed time. A score of 50,000 after 8 seconds can be rejected.
- Plausibility rules. Cap the maximum points per second, the maximum level reachable in a given time, and the number of submissions per player per hour.
- Replay validation. For puzzle or deterministic games, have the client send the inputs (moves, seed, timestamps) instead of the score, and let the server recompute the result. This is the strongest option and works well for games like tile puzzles or roguelikes with seeded randomness.
- Require a login for ranked play, so bans stick and rate limits apply per account.
Also check that players can't update or delete other players' scores. On Supabase, follow our RLS checklist. On Firebase, make sure your Firestore or Realtime Database rules don't contain if true for writes.
2. Multiplayer: make the server authoritative
In a client-authoritative game, each player tells everyone else "I'm at x, y and I hit you for 40 damage." That's easy to build and easy to cheat. A server-authoritative game works differently: clients send inputs ("pressed left", "fired"), and the server simulates the result and sends back the state.
You don't have to rebuild everything at once. Start by moving the things that matter most to the server:
- Damage, health and deaths.
- Currency, inventory and item drops.
- Win and loss results, and anything that affects rank.
- Movement speed limits: if a player moves farther between ticks than is possible, snap them back.
Frameworks like Colyseus, Nakama, PartyKit or a simple Node or Cloudflare Durable Objects server make this manageable for small games.
3. Don't send secrets to the client
If the client receives it, a player can see it. Common leaks in games:
- Positions of hidden or far-away enemies in a fog-of-war game. Only send what the player can currently see.
- The answer to a puzzle, quiz or word game shipped in the JavaScript or in an API response.
- Loot tables and upcoming random rolls. Do rolls on the server.
- Other players' emails, IPs or account IDs in lobby or chat data. Send display names only.
Search your bundle and API responses for these the same way you'd search for leaked keys.
4. Lock down realtime channels
Many vibe-coded multiplayer games use Supabase Realtime, Firebase or a WebSocket server for chat and game state.
- Supabase Realtime: public broadcast and presence channels can be joined by anyone with your anon key. Use private channels with Realtime Authorization, which applies RLS policies on
realtime.messages, so only players in a room can join and send. - WebSockets: authenticate the connection (a short-lived token in the first message or the connection URL), check which room the player belongs to on every message, and drop messages for rooms they aren't in.
- Validate every message. Check types, ranges and lengths. Reject a "move" with a distance of 10,000 or a chat message 50,000 characters long.
- Rate limit per connection. A client sending 500 messages a second is either broken or cheating. Disconnect it.
5. Remove exposed endpoints and keys
During development, AI tools often add helpers like /api/reset-leaderboard, /api/give-coins, /admin or a "god mode" flag. Search your project for these before launch and delete them, or protect them with a real admin check on the server.
- Check your bundle for keys to game services, payment providers or AI models. Only keys designed to be public (like a Supabase anon key or Firebase web config) belong in the client.
- If your game uses AI for NPC dialogue or level generation, the AI call needs to happen on your server, with rate limits. See our guide on protecting AI features from abuse.
- If players can chat with an AI character that can give items or change game state, read our prompt injection basics. Players will try to talk it into giving them everything.
6. Payments and items
- Grant purchased items only from a verified payment webhook, never from the client saying "purchase complete."
- Store inventory and currency on the server. The client can display them but never set them.
- Make trades and gifts atomic database transactions, so a player can't duplicate items by sending the same request twice at the same moment. Test this by firing two identical requests together from the console.
Be realistic about anti-cheat
You won't stop every cheater in a browser game, and you don't need to. The goal is to make cheating hard enough that it isn't worth it, keep cheats from hurting other players, and make cleanup easy. Keep logs of score submissions and match results with player IDs, so you can find and remove bad entries, and add a simple way to reset or ban an account.
FAQ
How do I stop fake scores on my leaderboard?
Don't let the client insert scores directly. Submit through a server function that checks a game session, elapsed time and plausibility, or recompute the score from the player's inputs.
Does obfuscating my game code stop cheaters?
Not really. It slows people down briefly. Anything that affects other players needs to be decided on the server.