⇐ Back to Blog

A PWA Can't Cache a Game It Doesn't Host. Settle That Before You License HTML5 Games.

Published on

Before you license HTML5 games for a PWA, settle hosting: a service worker cannot cache a game served from your licensor's domain, so offline play dies.

The install prompt is the part everyone demos. A manifest, a set of icons, a service worker, and inside a sprint your games portal has a home-screen icon and a splash screen. That work is real and it is cheap. The line underneath it on the slide — works offline, push notifications, app-store presence without app-store fees — is where a licensed catalogue behaves differently from a product you built yourself.

The difference is not effort. It is which origin the game is served from. Most catalogue licences default to the licensor hosting the builds and the licensee embedding them in an iframe. That default is fine for an ordinary website. It quietly removes about half of what a PWA is for, and nobody notices until the offline test.

🚧 The Service Worker Stops at the Frame Border

A service worker is registered against an origin and a path. It controls navigations and resource requests inside that scope, and nothing outside it. MDN's Service Worker API documentation is explicit about the boundary: requests made from a cross-origin iframe are controlled by a service worker registered on the iframe's origin, not the parent page's. Your worker never sees them.

So if the game runs at games.licensor.com inside a page at yourportal.com, your service worker can cache your shell, your artwork, your category pages and your fonts. It cannot cache one byte of the game. Offline, the player gets your app shell — header, categories, thumbnails, everything looking healthy — and then a browser error page inside the frame when they tap a title. That is a worse experience than having no offline mode at all, because the shell loading successfully is a promise you then break.

Two things follow that people get wrong in the same meeting:

  • A subdomain is a different origin. Moving builds to games.yourportal.com does not put them under the service worker registered at www.yourportal.com. Same company, same certificate, different origin. You need the builds under the same origin and inside the worker's path scope, or a second registration with its own manifest scope and its own cache.
  • Self-hosting is a licence question before it is an engineering question. Serving the build yourself means making a copy on your infrastructure. If your agreement says the licensor hosts and you embed, you do not have that right yet, however easy the file copy would be.

🧮 A Cross-Origin File You Cache Costs About 7 MB, Whatever It Weighs

Say you get part of the way there — the licensor keeps hosting, but you cache assets with a no-cors fetch to at least hold the images. That produces an opaque response: storable in the Cache API, unreadable from JavaScript, and padded heavily for quota purposes so that a site cannot infer cross-origin file sizes.

Chrome's own guidance on understanding storage quota puts the floor at roughly 7 MB per cached opaque response. A 6 KB sprite sheet counts as about 7 MB. Cache forty small assets from a domain you do not control and you have spent something in the region of 280 MB of the user's quota to store maybe a dozen megabytes of actual content. Teams find this by watching quota errors fire on mid-range Android and assuming the games are enormous.

The fix is not client-side. It is a delivery requirement on whoever serves the builds: correct CORS headers on the asset host so responses are transparent rather than opaque. Put it in the deliverables schedule in those words. "Please add Access-Control-Allow-Origin to the CDN serving the builds" is a five-minute change for a licensor and an unsolvable problem for a licensee.

💾 The Storage Ceiling Is Generous — and It Is Not a Promise

The headroom is better than the sceptical blog posts suggest. WebKit's updated storage policy, in force from Safari 17, sets the per-origin quota at up to 60% of total disk space for a browser app and up to 15% for other apps embedding WebKit, with an overall quota of 80% and 20% respectively. Crucially for this decision, WebKit states that a Home Screen web app running standalone gets the same origin quota and overall quota as it would in the browser. Chromium is in the same territory, reporting up to 60% of total disk per origin.

None of that is a guarantee. Quota is a ceiling under which the browser still evicts when the device is under storage pressure, and a licensed arcade catalogue is the wrong thing to try to store wholesale anyway. A thousand titles at even 8 MB each is 8 GB nobody will ever grant you and no player wants to spend. Treat offline as a shortlist feature: ten to twenty titles, deliberately chosen, pinned by the player or by you, refreshed on a schedule. Design the UI so it is obvious which games are available offline and which are not. The failure mode you are avoiding is a player on a plane discovering the distinction by trial and error.

🍎 Push Works on iOS — After the Install, and Only on a Tap

Web push arrived on iPhone with iOS 16.4 in March 2023, and it came with a gate: it works only for web apps the user has added to the Home Screen, and the permission prompt requires an explicit user gesture. There is no programmatic install prompt on iOS either — the player has to find Share, then Add to Home Screen, unassisted.

Stack that funnel honestly. Visit, then a manual multi-tap install through a menu most people have never opened, then a tap on your in-app button, then the system permission dialog, then a yes. A retention plan that assumes push on iOS is really a plan that assumes installs on iOS, and installs are a second conversion sitting on top of a visit you already had to earn.

Safari 18.4, in March 2025, added Declarative Web Push, which renders a push payload without waking a service worker to run JavaScript. It is genuinely less code and it is easier on battery. It does not move the install gate one inch. Budget engineering accordingly: the hard problem here is persuasion, not plumbing.

🇪🇺 The "Apple Killed PWAs in the EU" Line Is Two Years Stale

This one still shows up in vendor comparison pages and in AI-generated summaries, and it kills PWA proposals in meetings where nobody checks. In February 2024 Apple did say Home Screen web apps would stop working as standalone apps in the EU under the Digital Markets Act. After sustained criticism — Open Web Advocacy was among the loudest — Apple reversed on 1 March 2024, and iOS 17.4 shipped with Home Screen web apps intact in the EU, still built directly on WebKit.

So: EU users can install your games PWA, and it runs standalone. If someone shoots down a proposal citing the removal, they are citing a decision that was withdrawn before it ever shipped. Check the date on any PWA capability claim before you plan around it — this platform moves, and half the material written about it does not.

🏪 Putting the PWA in Play Turns It Back Into an Android Release

The "app-store presence without app-store development" pitch usually means a Trusted Web Activity: your PWA packaged with Bubblewrap or PWABuilder, shipped as an Android app that renders your site with no browser UI. Dropping the URL bar requires Digital Asset Links — an assetlinks.json on your domain carrying the SHA-256 fingerprint of the signing certificate for the package users actually receive. Get the fingerprint wrong and you ship an app with an address bar across the top of it.

Once it is in Play, it is an Android release with Android obligations: content rating, data safety declarations, target API level, and the testing gate. Google's Play Console Help states that developers with personal accounts created after 13 November 2023 must run a closed test with at least 12 testers opted in continuously for 14 days before applying for production access. That is a real four-week item on a launch plan, and it surprises teams who assumed the web wrapper let them skip Android process entirely.

Worth saying plainly, because it cuts against the direction of this post: if Play distribution is the actual objective rather than a nice-to-have, licensing native Android builds is usually the shorter road. You get store-native listings per title, Play billing, and no wrapper policy exposure. A TWA is the right answer when the web portal is the product and the store icon is a convenience — not when the store is the business.

📄 Six Clauses That Decide Whether a Games PWA Is Buildable at All

Raise these before the builds land, not after your engineers hit the frame boundary:

  1. Hosting right. May you serve the build from your own origin, including subdomains, and may you put it behind your own CDN? This one clause decides whether offline is possible.
  2. Device-local caching. Is storing the build in a service worker cache on an end user's device a permitted reproduction? Most licences are silent, and silence is not permission when you are making persistent copies on hardware you do not own.
  3. Header control. If the licensor hosts, who can set CORS and cache headers on the asset domain, and how long does a change take?
  4. The offline-eligible subset. Which titles have no runtime network dependency — no ad SDK call, no leaderboard write, no remote config fetch? Ask for the list. Do not discover it by testing a thousand builds.
  5. Storage keys. Saves are written under keys scoped to the origin the game runs on. Move a title from the licensor's domain to yours and every player's progress resets. Decide whether you migrate, announce, or accept it.
  6. Update mechanism. When the licensor patches a build, how does a copy already cached on a player's device learn about it? Versioned paths, not overwrite-in-place, or you will serve a fixed bug for months.

🚫 Five Ways Operators Build a Games PWA Nobody Installs

  • Firing the install prompt on first visit. Before the player has finished a single game they have no reason to want your icon on their home screen. Gate it behind a second session or a completed play.
  • Promising offline when only the shell is cached. Covered above, and the single most common version of this mistake.
  • Treating a subdomain as the same origin. Costs a sprint to discover and a redesign to fix.
  • Building the PWA before there is any traffic. An install is a conversion on a visit. Zero visits convert to zero installs, and the wrapper does not generate demand.
  • Wrapping in a TWA to dodge store fees without a billing plan. Play billing rules still apply to what you sell inside it. Decide the payment route before you package, not after a policy review.

🎯 Hosting Rights Are the Clause That Matters When You License HTML5 Games

A licence from a direct licensor is a scope conversation, not a file handover. What you are negotiating for is the set of rights the sections above depend on: HTML5 builds you may serve from your own origin, APK builds where the surface calls for them, source access where applicable, branding and reskin permissions, and hosting options that include your infrastructure rather than only the licensor's. Forestry Games has licensed games since 2017 and develops HTML5 titles in-house, which is what makes delivery questions — CORS headers, versioned build paths, a named offline-eligible list — answerable rather than escalated.

A catalogue of 1,049 titles maps onto a PWA in two layers: a pinned offline shortlist of ten to twenty, and a browsable online remainder that carries the session length. You can license HTML5 games for the web layer and buy Android games for the store layer out of the same conversation, which is usually cheaper than running two supplier relationships to reach the same players twice.

🧸 Licensing Branded Games When the Wrapper Is Yours

Forestry Games works with branded IP and has brand partnerships including Disney, Nickelodeon, Cartoon Network and Warner Bros, and businesses can license branded game content through it for campaigns, portals, events and apps. One planning note specific to this post: an installed web app puts brand-adjacent assets on surfaces a website never touches — a home-screen icon, a splash screen, a push notification, a store listing. Branded licences generally carry usage approvals, so raise those surfaces during scoping rather than after the icon is designed.

If you are sizing a portal or an installable app, browse the catalogue to see what depth looks like, or ask for a licence scope that names hosting and device-caching rights explicitly. A membership is the straightforward route when you want breadth first and will pick the offline shortlist later.

🧭 Decide the Origin Before the Builds Land

Almost everything difficult about a games PWA collapses into one question asked early: whose domain serves the game? If it is yours, offline play, sensible quota behaviour, coherent storage and a real update story are all engineering work you can schedule. If it is the licensor's, you are building an installable bookmark — which is a perfectly respectable product, and one you should describe as such internally instead of promising an offline arcade you cannot ship.

The next thing to do is not to write a service worker. It is to open your current agreement, or your draft one, and find the sentence about where builds are hosted. If it does not exist, that is the clause to add before anything else. Ask your licensor for a scope that names self-hosting and device-local caching, and for the list of titles with no runtime network dependency — then buy HTML5 games against a plan whose offline promise you can actually keep.

Related Reading

"White-Label" Is Four Different Purchases. Decide Which One Before You License HTML5 Games.

You Play Twenty Titles Before You License HTML5 Games. Pick Them by Failure Mode, Not by Fun.

Poki Runs on Search. CrazyGames Runs on Direct. Decide Yours Before You Buy HTML5 Games.

Film and TV Slipped to 33% of Character Licensing. Look at the Other 34% Before You License Branded Games.

Build, Commission or License HTML5 Games: The Answer Is Titles Per Month, Not Cost Per Title

Ramadan 2027 Runs 8 February to 8 March. The Window to License HTML5 Games for It Closes in October.

Browse all posts →