Quick answer: The browser blocked storage for your game's frame, usually because it runs in a cross-site iframe (itch.io, Newgrounds, game portals) and the player blocks third-party cookies, or the iframe is sandboxed without allow-same-origin. Wrap every storage access in try/catch with an in-memory fallback, tell players progress will not persist, and offer a full-window link or the portal's own save API.
This error stops a web game at startup on some players' machines and works perfectly on yours. The difference is a browser privacy setting, and the fix is making your game survive without storage instead of assuming it is always there.
The exact errors
// Chrome, Edge
Uncaught SecurityError: Failed to read the 'localStorage' property from 'Window': Access is denied for this document.
// Firefox, Safari
SecurityError: The operation is insecure.
The error is thrown by merely touching window.localStorage, not by reading a key. Code like const s = localStorage; at the top of a module crashes the whole script.
Why some players are blocked
- Cross-site iframes. On itch.io and most portals, your game is served from a different domain inside an iframe. Browsers treat its storage as third-party. With third-party cookies blocked (a setting many players turn on, and the default in some browsers and modes), access throws.
- Sandboxed iframes. An iframe with a
sandboxattribute but withoutallow-same-originhas an opaque origin and no storage at all. - Privacy modes and extensions can block or wipe storage.
Even where access does not throw, many browsers partition third-party storage per top-level site, so saves made in the embed are separate from saves made when the game is opened directly.
Fix 1: never touch storage without a guard
const storage = (() => {
const memory = new Map();
let ls = null;
try {
ls = window.localStorage;
const probe = '__probe__';
ls.setItem(probe, '1');
ls.removeItem(probe);
} catch (e) {
ls = null; // blocked, sandboxed or full
}
return {
persistent: ls !== null,
get(k) { try { return ls ? ls.getItem(k) : (memory.get(k) ?? null); } catch { return memory.get(k) ?? null; } },
set(k, v) { try { ls ? ls.setItem(k, v) : memory.set(k, v); } catch { memory.set(k, v); } },
};
})();
Use storage.get and storage.set everywhere, including in libraries you configure. The game now runs either way.
Fix 2: tell the player saves will not stick
If storage.persistent is false, show a small notice on the title screen: progress will not be saved in this browser, with a link to open the game in its own tab. On itch.io, enabling the fullscreen button or offering the game's direct URL gives it first-party storage. Some browsers also support the Storage Access API (document.requestStorageAccess()), which an embedded game can call from a click to ask for its storage; support and behavior vary, so treat it as an enhancement.
Fix 3: use the platform’s save API on portals
Web game portals with their own SDKs usually provide data storage tied to the player's account on the portal, which works regardless of iframe restrictions. If you publish on such a portal, use its storage for saves and keep local storage as a fallback.
Engines and IndexedDB
Unity WebGL (PlayerPrefs and persistentDataPath) and Godot's web export (user://) store data in IndexedDB rather than localStorage. In the same blocked situations IndexedDB is usually blocked too, so saves fail or vanish. The same idea applies: detect it, keep the game running, and tell the player. The localStorage error itself mostly comes from your own JavaScript, plugins and analytics scripts.
Seeing which players are affected
The players who hit this are on specific browsers and settings you do not use. Bugnet's web SDK is one script tag: it listens for uncaught errors and unhandled promise rejections and files them with a screenshot of your canvas, the browser and device, and a performance snapshot, grouped so you see how many players each one hits. A storage SecurityError that slips past your guard arrives with the browser and embedding page, and you can report storage.persistent as metadata to see how many players are running without saves.
Related problems
- How to Fix a localStorage Quota Exceeded Error in a Web Game
- How to Fix “QuotaExceededError” When Saving Web Game Data in localStorage
- How to Fix a Web Game Not Loading After Uploading the Zip to itch.io
Frequently asked questions
Why does my web game get “Access is denied” for localStorage?
It runs in a cross-site iframe, such as on itch.io, and the player’s browser blocks third-party storage, or the iframe is sandboxed without allow-same-origin.
How do I use localStorage safely in a web game?
Wrap every access, including reading window.localStorage itself, in try/catch, and fall back to in-memory storage when it throws.
Can an embedded game get its storage back?
Sometimes. Opening the game in its own tab gives it first-party storage, and some browsers support document.requestStorageAccess() from a user click.
Touching window.localStorage can throw. Guard the property access, not just the calls.