[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$f28eej4bay2g79":3},{"_id":4,"slug":5,"title":6,"subtitle":7,"kind":8,"cards":9,"tags":44,"categories":46,"source":48,"lang":51,"author":52,"audioState":55,"stats":56,"publishedAt":59,"renderer":60},"6abacec6ca21c797c7e9a516","building-a-bilingual-browser-game-with-astro-and-cloudflare-ce1f33c6","Building a bilingual browser game with Astro and Cloudflare (and making the HTML actually cache)","I recently shipped Cá Viên Chiên, a small cooking game about Vietnamese fried fish ball street carts.","news",[10,14,19,24,29,34,39],{"headline":11,"body":12,"imageUrl":13,"sourceImageUrl":13},"Building a bilingual browser game with Astro and Cloudflare (and making the…","I recently shipped Cá Viên Chiên, a small cooking game about Vietnamese fried fish ball street carts. It runs in the browser, works best on phones, and ships in Vietnamese and English. Here are the parts that were more interesting than I expected.","https:\u002F\u002Fmedia2.dev.to\u002Fdynamic\u002Fimage\u002Fwidth=1200,height=627,fit=cover,gravity=auto,format=auto\u002Fhttps%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F395tbs4u48tchti81rty.png",{"headline":15,"body":16,"imageUrl":17,"images":18},"Most of the site isn't the game","Most of the site isn't the game — it's the landing page, guide, tips, menu and FAQ in two languages. Those pages matter for search, so they should be plain static HTML. Astro gives me that, and the game itself is a single island of vanilla JS (~26 KB) with no framework runtime. CSS is under 5 KB. i18n routing: \u002Fvi\u002F and \u002Fen\u002F","\u002Fapi\u002Fmedia\u002Fposts\u002Fbuilding-a-bilingual-browser-game-with-astro-and-cloudflare-ce1f33c6\u002F1.webp",{"local":17},{"headline":20,"body":21,"imageUrl":22,"images":23},"Each language lives under its own prefix, with","Each language lives under its own prefix, with hreflang alternates pointing at each other. The root \u002F redirects based on Accept-Language, which turned out to be the one thing I couldn't cache cleanly (more below).","\u002Fapi\u002Fmedia\u002Fposts\u002Fbuilding-a-bilingual-browser-game-with-astro-and-cloudflare-ce1f33c6\u002F2.webp",{"local":22},{"headline":25,"body":26,"imageUrl":27,"images":28},"The game saves to localStorage after every purchase","The game saves to localStorage after every purchase and at the end of each day. To move to another device, players copy a save code — the serialized state — and paste it on the other side. No backend, no sign-up. Getting HTML to actually hit the edge cache","\u002Fapi\u002Fmedia\u002Fposts\u002Fbuilding-a-bilingual-browser-game-with-astro-and-cloudflare-ce1f33c6\u002F3.webp",{"local":27},{"headline":30,"body":31,"imageUrl":32,"images":33},"Static assets under _astro\u002F* are hashed and served","Static assets under _astro\u002F* are hashed and served with max-age=31536000, immutable, so those were fine from day one. HTML was the slow part: every request showed cf-cache-status: DYNAMIC and ~250 ms TTFB going back to origin.","\u002Fapi\u002Fmedia\u002Fposts\u002Fbuilding-a-bilingual-browser-game-with-astro-and-cloudflare-ce1f33c6\u002F4.webp",{"local":32},{"headline":35,"body":36,"imageUrl":37,"images":38},"Adding a Cloudflare Cache Rule for \u002Fvi\u002F* and","Adding a Cloudflare Cache Rule for \u002Fvi\u002F* and \u002Fen\u002F* flipped the status to HIT — but TTFB barely moved. The origin was still sending cache-control: public, max-age=0, must-revalidate, and with the rule set to respect origin headers, the edge TTL was effectively zero: every request revalidated.","\u002Fapi\u002Fmedia\u002Fposts\u002Fbuilding-a-bilingual-browser-game-with-astro-and-cloudflare-ce1f33c6\u002F5.webp",{"local":37},{"headline":40,"body":41,"imageUrl":42,"images":43},"The fix I'm rolling out is CDN-Cache-Control, which","The fix I'm rolling out is CDN-Cache-Control, which Cloudflare honours ahead of Cache-Control, while browsers keep seeing max-age=0: Then purge \u002Fvi\u002F and \u002Fen\u002F in the deploy step so new releases show up immediately. Move the language redirect on \u002F to the edge so it can be cached per language Self-host the font to drop two extra origins If you want to see the result, the game is at cavienchien.net. Feedback on the difficulty curve is very welcome. For further actions, you may consider blocking this person and\u002For reporting abuse","\u002Fapi\u002Fmedia\u002Fposts\u002Fbuilding-a-bilingual-browser-game-with-astro-and-cloudflare-ce1f33c6\u002F6.webp",{"local":42},[45],"dev",[47],"Technology",{"name":49,"url":50},"Dev.to","https:\u002F\u002Fdev.to\u002Focxers\u002Fbuilding-a-bilingual-browser-game-with-astro-and-cloudflare-and-making-the-html-actually-cache-3o2b","en",{"handle":53,"displayName":54},"spots","Spots","queued",{"views":57,"likes":58,"saves":58,"shares":58,"completions":58,"opens":58,"skips":58,"depthSum":58},3,0,"2026-09-28T20:32:06.641Z","local"]