This post was written with AI assistance. The code, numbers and site are real.
Easy Free Games is a browser games site with 3,800 HTML5 games in 9 languages. It has no framework, no database and no server code. One Python script writes plain HTML files, and Cloudflare Pages serves them.
Here's what that setup looks like, and the parts that were harder than expected.
The numbers
- 9,433 HTML pages from one build
- About 22 seconds per build when the game feeds are already cached
- 9 languages: English at the root, the others in subfolders like
/fr/and/ar/ - About 2,500 lines of Python in
build.py
Why static
A games portal is almost all read-only pages. Nothing changes between requests, so there's no reason to render anything at request time. Static files are cheap to host, fast from a CDN, and easy for search engines to crawl.
The build reads the game feeds, picks and sorts the games, then writes every page. Translations live in one JSON file per language, and a small check script flags missing or suspicious strings before anything ships.
Not every page should be indexed
Generating 9,000 pages is the easy part. Asking Google to index all of them on a new domain is a bad idea, because thin pages can drag down the good ones.
So the build has a gate:
- English indexes the top games, plus any game with hand-written content.
- Other languages index a smaller shared set of about 370 games.
- Page 2+ of list pages get
noindex,followand stay out of the sitemaps.
Every language uses the same set of games, which keeps the hreflang links consistent. One bug worth mentioning: a hub page recommended games that had no page in that language. Nothing looked broken, because the links quietly fell back to English. A small script that compares the recommended games with the generated files caught it.
Games in a locked iframe
The site embeds games from other providers, so each game runs in a sandboxed iframe:
<iframe sandbox="allow-scripts allow-same-origin allow-pointer-lock
allow-orientation-lock allow-forms allow-modals allow-presentation
allow-popups allow-popups-to-escape-sandbox"></iframe>
Leaving out allow-top-navigation means a game can't redirect the whole page. allow-popups is there so a click on an ad can still open a new tab.
We also learned to test ads in a clean browser. They looked broken for a while, and the sandbox was the first suspect. The real cause was that the test browser was blocking the ad servers on every site.
The widget
Site owners can show games on their own pages with two lines:
<div class="efg-widget" data-type="top" data-n="6" data-theme="dark">
<a href="https://easyfreegames.com/">Free online games</a>
</div>
<script async src="https://easyfreegames.com/widget.js"></script>
The script is 3.7 KB, or about 1.8 KB gzipped. A few decisions kept it simple:
- Shadow DOM: the host page's CSS can't break the widget, and the widget's CSS can't leak out.
- No iframe, no cookies, no tracking: it renders thumbnails that link to game pages.
- Static JSON data: the build writes the data files, so the widget needs no API.
-
A real fallback: the link inside the
divstays there if the script never runs.
You can try the options on the widget page. There's also a free gamepad tester, which uses the browser Gamepad API.
What I'd do again
For a site that's mostly read-only, a single build script has been easy to change and easy to check. The hard problems were not technical. They were deciding which pages deserve to exist, and proving that things work instead of assuming they do.
Top comments (0)