Hardware FixRecommendedDevice not working? Your driver may be the problemCheck updates for common hardware issues.Fix DriversOctober DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsClean PCRecommendedOne scan can reveal what keeps slowing WindowsLook for cleanup and repair opportunities.Run Scan×
Skip to content
World desk9 min

Building a Browser Game with Astro, Cloudflare Workers, and a Verifiable D1 Leaderboard

Deploy Astro on Cloudflare Workers, store scores in D1 with batched writes, and see why a transactional leaderboard still needs a rule-based way to verify scores.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A leaderboard built on Astro, Cloudflare Workers, and D1 can store every score reliably, and related writes can be made atomic. What it cannot do on its own is show that a score was earned. D1 transactions guarantee that a group of database changes succeeds or rolls back together. They say nothing about whether the number a browser sent reflects real play.

In this guide, “verifiable” has a narrow engineering meaning: the server can check each submitted score against rules that it enforces. Cloudflare’s documentation covers deployment, SQL access, and transactions. It does not specify a protocol that proves browser-submitted scores are authentic, so that layer is a design decision you make for your own game.

As an Amazon Associate I earn from qualifying purchases.

What “verifiable” has to cover

Four properties are often grouped under “verifiable.” They are separate, and the platform provides only some of them.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Property Question it answers What the platform provides What you build
Durability Was the record saved? D1 is a serverless SQL database that a Worker reaches through a binding Schema, retention, and an export plan
Atomicity Did all related rows change together? batch() runs statements in sequence as a transaction and rolls back when one fails Deciding which writes belong in one batch
Input validity Is the request well formed and within range? Not provided by D1 or Workers Type, range, and size checks in the route that receives the score
Score authenticity Did the game actually produce this score under its rules? Outside what the platform guarantees A mechanism tied to your game’s rules, covered below

A board with the first two properties is consistent. It becomes verifiable only when the fourth is also enforced, and that depends on the game.

#1 Best Overall
Sale
EasySMX X15 Wireless PC & Steam Gaming Controller, Hall Effect, Black | Hall Effect Joysticks, No Drift, 2 Back Buttons, RGB, 1000mAh, 20H Playtime, PC/Steam/Switch/Android
  • Platform Compatibility: This PC controller is designed for Windows PC, Steam, Switch, Android, and iOS. Xbox-style asymmetric stick layout for PC gamers. Three modes cover all your devices. Please check your device compatibility before purchase
  • Three Connection Modes: 2.4G wireless, Bluetooth, wired USB-C. PC gets native XInput/DirectInput. Switch pairs via Bluetooth, no adapter. This gaming PC controller switches devices seamlessly. Stable wireless minimizes random disconnects during gaming
  • Hall Effect Precision: Hall effect joysticks and triggers eliminate stick drift. This gaming controller for PC delivers smooth, responsive input with no dead zones. Built for FPS, racing, and action games. Long-term precision for competitive PC gaming
  • Back Buttons & Battery: Two programmable back buttons map combos and shortcuts. Textured grips with dual vibration. 1000mAh battery delivers up to 20H playtime. RGB can be turned off. A solid PC controller for gaming with custom back buttons
  • ABXY Layout Switch: Press B + Minus + Plus to swap between PC and Switch modes. Features: 1000Hz polling rate, RGB lighting, turbo. Note: designed without mic jack or gyro sensor

How the pieces fit together

  • Game page: a static Astro page that loads the game’s JavaScript and runs the game in the browser.
  • Score endpoint: an on-demand Astro route running in a Cloudflare Worker. It validates the submission and writes it to D1.
  • Leaderboard endpoint: an on-demand route that reads the top rows at request time.
  • D1 database: holds every accepted submission and one best-score row per player.
  • Durable Objects (optional): coordinate live match state if the game needs it. They are compared with D1 in the limits section.

The browser never receives database credentials. It sends a request to the score endpoint, and only the Worker talks to D1.

Set up the Astro project for Cloudflare

Choose a rendering mode for each page

Astro’s Cloudflare deployment guide supports full-stack applications on Workers. Cloudflare’s Astro guide says the adapter sets server output by default, and individual pages can be prerendered when they do not need on-demand rendering. A fully pre-rendered site does not need the adapter and can be deployed as static assets.

Page or route Rendering Reason
Game page Prerendered with export const prerender = true; The page shell and game assets do not change per request
Leaderboard display Fetched by the browser from the leaderboard endpoint on load Prerendered HTML would freeze the scores that existed at build time
Score and leaderboard endpoints On demand (the server default) They read or write D1 on each request
About or privacy page Prerendered No per-request data

Install the adapter and confirm the preview

  1. Create the project with npm create astro@latest and choose a template.
  2. Install the adapter with npm install @astrojs/cloudflare.
  3. Register the adapter in astro.config.mjs:
    import { defineConfig } from 'astro/config';
    import cloudflare from '@astrojs/cloudflare';
    
    export default defineConfig({
      adapter: cloudflare(),
    });
  4. Open the Wrangler configuration that Astro generated and keep its compatibility date. Take that value from your generated file, not from an older tutorial.
  5. Run the local preview described in Astro’s Cloudflare deployment guide, then deploy with npx wrangler deploy.

If hydration errors appear after deploying

Astro’s deployment guide notes that Cloudflare Auto Minify can cause client-side hydration mismatches. The documented fix is to disable Auto Minify for the site. If the browser console reports hydration mismatches after deployment, turn it off in Cloudflare’s dashboard for the site and reload.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Create the D1 database and schema

  1. Create the database with npx wrangler d1 create leaderboard. The command prints the database ID you need in the next step.
  2. Bind the database in your Wrangler configuration. The binding name is yours to choose, but the Worker code must use the same name. In TOML:
    [[d1_databases]]
    binding = "DB"
    database_name = "leaderboard"
    # add the database_id printed by the create command

    If your project uses wrangler.jsonc, the same entry goes in a d1_databases array.

  3. Create schema.sql:
    CREATE TABLE IF NOT EXISTS scores (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      player_id TEXT NOT NULL,
      score INTEGER NOT NULL,
      submitted_at INTEGER NOT NULL
    );
    
    CREATE TABLE IF NOT EXISTS best_scores (
      player_id TEXT PRIMARY KEY,
      best INTEGER NOT NULL
    );
    
    CREATE INDEX IF NOT EXISTS idx_best_scores_best ON best_scores (best DESC);
  4. Apply the schema to the remote database with npx wrangler d1 execute leaderboard --remote --file=./schema.sql. Use --local to apply it to the local development database instead.

The scores table keeps every accepted submission, which is what you would audit if a result were disputed. best_scores holds one row per player, and the index serves the descending sort the leaderboard performs.

Accept score submissions in a server route

The game page sends a JSON body to the score endpoint. The route validates the body and then passes the values to a write function. Validation comes first, because a transaction can only preserve what it is given.

Validate the request

export function parseSubmission(body: unknown) {
  if (typeof body !== 'object' || body === null) return null;
  const { playerId, score } = body as Record<string, unknown>;
  if (typeof playerId !== 'string' || playerId.length === 0 || playerId.length > 32) return null;
  if (typeof score !== 'number' || !Number.isInteger(score) || score < 0 || score > 1000000) return null;
  return { playerId, score };
}

The 1,000,000 ceiling is only an example. Derive the real bound from your game’s rules. A range check rejects malformed input, but it cannot tell you whether a well-formed score was earned.

Write the submission with db.batch()

Bind values through ? placeholders and .bind(), never by building SQL strings from request data. The following function writes the raw submission and updates the player’s best score as one unit:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #2
Logitech G F310 Wired Gamepad Controller Console - Blue/Black | Console-like layout, 4-switch D-pad
  • With broad game support, the Logitech Gamepad F310 works with old standbys to today's biggest titles, so it's easy to set up and use with your favorite games.
  • Profiler software allows the gamepad to be programmed to perform keyboard and mouse commands for games without gamepad support.* * Requires software installation.
  • A familiar control layout that doesn't require a learning curve to be able to use, with all the same buttons as on an Xbox 360.
  • The unique floating D-pad rests on four switches-instead of a single pivot point-making it responsive to quick changes in direction.
  • The six-foot cord lets you lean back and play a comfortable distance from your PC monitor.
export async function saveScore(db: D1Database, playerId: string, score: number) {
  const submittedAt = Date.now();
  await db.batch([
    db
      .prepare('INSERT INTO scores (player_id, score, submitted_at) VALUES (?, ?, ?)')
      .bind(playerId, score, submittedAt),
    db
      .prepare(
        'INSERT INTO best_scores (player_id, best) VALUES (?, ?) ' +
        'ON CONFLICT(player_id) DO UPDATE SET best = MAX(best, excluded.best)'
      )
      .bind(playerId, score),
  ]);
}

Wire the pieces together in the route in this order:

  1. Parse the request body as JSON and return a 400 response if parsing fails.
  2. Pass the result to parseSubmission and return a 400 response if it returns null.
  3. Call saveScore with the D1 binding and catch any error. Return a 500 response so the game can show a retry message.
  4. Return a success status only after saveScore resolves.

The read path is a single query against the best-score table:

SELECT player_id, best FROM best_scores ORDER BY best DESC LIMIT 10;

Serve it from the leaderboard endpoint, and have the browser fetch it on load so the displayed scores stay current.

What batch() guarantees, and what it does not

Cloudflare’s D1 documentation states the guarantee in one sentence: “Batched statements are SQL transactions.” (Cloudflare, D1 Database documentation.) In practice, the statements in a batch run in order, and a failing statement aborts the group with rollback. That keeps the raw submission and the best-score row consistent with each other, so a crash between the two writes cannot leave them out of step.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The guarantee stops there. A client that submits a fabricated 999999 through the same route is stored just as cleanly as an honest result. Everything the route accepts becomes a consistent row, so the authenticity decision has to be made before the batch is called.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Designing score authenticity

This guide does not choose a mechanism, because the right one depends on details it does not know: what the game computes, whether its outcomes are deterministic, and how much server work a single result costs. Three approaches are common, listed from the lightest to build to the heaviest. None of them is specified by Cloudflare’s documentation.

Plausibility checks and rate limits

Check that a score is achievable in the elapsed time, throttle submissions per player, and reject values above a ceiling derived from the game. These checks are cheap and catch careless or casual tampering. They do not establish that a score came from play, because anyone who can read the request format can submit a value that passes them. The daily request cap on Workers is a plan limit, not a rate-limit design, so per-player throttling still needs your own logic.

Rank #3
GameSir G7 SE Wired Controller for Xbox Series X|S, Xbox One & Windows 10/11, Plug and Play Gaming Gamepad with Hall Effect Joysticks/Hall Trigger, 3.5mm Audio Jack (White)
  • Versatile compatibility: supports Xbox Series X/S, Xbox One X/S consoles and PC Win10 and above (including the game platform Steam).
  • Precise control: features Hall joysticks and Hall triggers for a comfortable feeling, long service life and improved game accuracy.
  • Plug and Play Convenience: Wired USB connection (removable) for easy setup and instant play without the need for additional drivers.
  • Customizable experience: Includes 2 custom backbuttons that allow users to eliminate false triggers and improve their gaming experience.
  • Impressive gameplay: Provides a pulsating vibration trigger and an asymmetric vibration grip motor for intense tactile feedback.

Replay verification

The client records its inputs, and the server re-runs the game from the same seed to reproduce the score. The server should issue a fresh seed for each run, so an old recording cannot be resubmitted. Replay checking requires deterministic game logic: the same inputs must produce the same result on the server and in the browser. That constrains the engine and physics code you can use unless you control the timestep and randomness yourself.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

What it proves is narrower than its name suggests. It shows that the recorded input sequence produces the submitted score under the game’s rules. It does not show that a person pressed those keys, so a bot that sends legitimate inputs passes.

Server-authoritative simulation

The server runs the game rules itself. The browser sends inputs, and the server computes the score, so the submitted number is never trusted. This removes client-side score forgery, but it moves the game logic into the Worker and adds a workload to every submission. Replay verification carries the same cost, because it also runs the game logic inside a Worker invocation. The per-invocation CPU limit in the next section therefore applies to both. A fast-paced game that must simulate many frames per submission may not fit the Free plan’s 10 ms limit, while the Paid plan’s listed limit is 5 minutes.

Choosing a mechanism and labeling the board

  • If the score can be recomputed from inputs within your per-invocation CPU budget, use server-authoritative simulation.
  • If the game is deterministic but the simulation is too heavy for one request, replay verification may still fit. Its cost depends on the size of the input log and the cost of the simulation, so measure both on your own game before committing.
  • If neither fits, use plausibility checks and label the board as self-reported rather than verified.

Scale and platform limits

The figures below are Cloudflare’s published limits as of the dates shown. They are useful for planning, but they describe plan allowances, not how fast your game will run.

Limit Workers Free Workers Paid Source and date
Maximum D1 database size 500 MB 10 GB Cloudflare D1 limits page, last updated April 21, 2026
Requests per day 100,000 No request limit Cloudflare Workers limits page, last updated September 5, 2026
CPU time per invocation 10 ms 5 minutes Cloudflare Workers limits page, last updated September 5, 2026

D1 is documented as single-threaded per database: each database processes queries one at a time. Concurrent score submissions therefore queue behind each other. Batching keeps related statements together, but it does not run them in parallel. Keep the number of statements per submission small, and index the columns your leaderboard query sorts by. The D1 documentation includes a rough throughput illustration based on query duration. Treat it as arithmetic rather than a forecast for your game, because write frequency, index design, and query cost all change the outcome.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Durable Objects or D1 for game state

Cloudflare’s storage comparison names Durable Objects as suitable for real-time collaboration, including game-server workloads, and describes D1 as a serverless SQL database. The two address different needs.

Concern D1 Durable Objects
Fit described by Cloudflare Serverless SQL database Real-time collaboration, including game-server workloads
Natural use in this stack Durable score records and leaderboard queries Coordinated live game state shared by several clients
Limits in the storage comparison 500 MB (Free) and 10 GB (Paid) per database, one query at a time per database Not stated in Cloudflare’s storage comparison

A common split, offered here as a design suggestion rather than a Cloudflare recommendation, keeps live match state in a Durable Object and writes only finished, validated results to D1.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
Outdated Drivers Are Slowing You DownFree scan - exact matches
PC Slower Than It Used to Be?Free scan - under a minute

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.