miinideckmiinideck
PricingUse casesBlog
Sign in
How-to & formats

It works when I open the file. It breaks once it's a link.

The page runs perfectly from your desktop, then one button stops working on the hosted version — usually fullscreen, copy-to-clipboard, or a fetch. Four causes, each with a one-line test you can run in the browser console to tell them apart.

By miinideck·September 12, 2026·6 min read
TL;DR
  • "Works from my desktop, breaks as a link" is almost never a bug in your page. It is one of four environment differences, and they have completely different fixes — so identify which one before you change any code.
  • The fastest triage is two lines in the browser console on the hosted page: window.self === window.top and document.fullscreenEnabled. Together they separate "the frame around my page is the problem" from "something inside my page is the problem" in about ten seconds.
  • The four causes: the host frames your page and withholds capabilities · https blocking http sub-resources · an origin that changed from null to your real domain · and the handful of things that only ever worked because you were on file://.

You built the thing. You double-clicked it, and it ran — the chart drew, the fullscreen button expanded, the copy button copied. You uploaded it, sent the link, and the fullscreen button now does nothing at all. No error, no message. Just a button that has stopped meaning anything.

The instinct at this point is to go back into the code and start rewriting the button. That is usually wasted work, because the button is almost certainly fine. What changed is everything around it.

Run these two lines first

Open the hosted link, open the browser console, and type these:

window.self === window.top   // false = your page is inside somebody's frame
document.fullscreenEnabled   // false = fullscreen was not granted to that frame

Ten seconds, and you have split the problem in half. If the first line says false, the page is being displayed inside a frame that the host controls, and a whole family of features are governed by that frame rather than by your code. If it says true, your page is its own document and the cause is somewhere in the next three sections.

This is worth doing before anything else because the two halves have no fixes in common. One is solved by changing where the page is served; the other by changing what is in it.

Cause 1: the host frames your page, and a frame has to be granted capabilities

A page inside an iframe does not automatically get everything a normal page gets. Fullscreen, clipboard writes, autoplay with sound, pointer lock, and downloads are all capabilities that the containing page has to hand down explicitly. If it doesn't, the API doesn't throw a loud error — requestFullscreen() returns a promise that rejects, and a button wired to it looks broken rather than blocked.

That is why these features tend to fail in a group. If fullscreen and copy-to-clipboard both stopped working at the same time, stop looking for a common bug in your code; there isn't one. They have a common gatekeeper.

The fix is not in your page. It is to serve the page as its own top-level document. Some viewers frame uploaded files deliberately, to keep the host's own chrome on screen — a reasonable design choice that happens to cost you these APIs.

Here your page is served as its own document, not inside a frame, so fullscreen and the rest behave the way they did on your desktop. Drop the file and get a private link. Free to try.

Try it free (no signup)

Cause 2: https blocks http, and your desktop never did

A hosted page is served over https. Browsers refuse to load scripts, stylesheets and images that arrive over plain http into an https page — mixed content. It is mostly silent: a line in the console, and the resource simply isn't there.

Opening the file from your desktop is not an https page, so none of that applies and every http:// URL loads fine. This is the single most common reason a page that looked complete locally arrives hosted with a missing chart library or a broken image.

Search your file for http://. Anything that isn't https:// is a candidate. Either switch it, or — better for a file you intend to send — embed the asset so there is nothing to fetch at all.

Cause 3: your origin changed, and somebody else's API noticed

This one runs in the opposite direction from what people expect, which is why it takes so long to find.

When you open a file from your desktop, requests your page makes carry an Origin of null. Hosted, they carry your real domain. An API that was quietly permissive about null — or that had no origin check at all in the path you were hitting — can now reject you outright, by CORS.

The console message names the origin it objected to. blocked by CORS policy plus your domain means the other end has to allow it; that is their configuration, not yours.

The mirror image is worth knowing too, because it confuses people going the other way: fetch('./data.json') fails locally and works hosted. A file:// page has no meaningful origin to resolve a relative request against, so the browser blocks it. Same for <script type="module" src="./thing.js">. If a page only misbehaves on your desktop, this is usually why — and it will fix itself the moment you host it.

Cause 4: it only worked because you were on file://

A smaller category, but real. localStorage on file:// behaves oddly — in some browsers every local file shares one bucket, so a page that appeared to remember state was reading something another file wrote. Once hosted, it gets its own proper storage and looks like it lost everything.

If the symptom is "it forgot my data", check this before assuming the hosting broke storage. Nothing broke; it just stopped sharing.

The triage, in order

  1. window.self === window.top → false? It's the frame. Serve it as its own document.
  2. document.fullscreenEnabled → false with top === self? Rare; check for a Permissions-Policy header from the host.
  3. Search the file for http:// → mixed content.
  4. Read the console's CORS line → the origin changed; the other end has to allow it.
  5. Only broken locally, fine hosted? That is file:// doing its job, not a hosting problem.

Almost every "it worked on my machine" for a self-contained page lands in one of those five. What they share is that the page is the same; the environment isn't. Which is also the argument for keeping the page genuinely self-contained — the fewer things it reaches out for, the fewer of these you can hit.

For the related case where the page is fine and the recipient's setup is the problem, see why a page opens on desktop but not on their phone, and for the format question underneath all of this, how to check your page on a phone before you send it.

More in How-to & formats

How do I edit a password-protected HTML page without re-encrypting it?

Client-side encryption tools turn your page into an encrypted file, so the protection and the artifact are the same object — every edit means re-running the tool and re-uploading. What that actually costs, the salt setting that decides whether your old share links survive, and when a hosted password is the better trade.

September 25, 2026·6 min read

How do I hand off client work that lives in a git repo?

Running the studio out of one folder per client is a good structure for making the work. It stops at the point where the client has to open it — a private repo needs a GitHub account, and Pages built from one is public by default. What the handover step actually needs, and how to add it without breaking the folder.

September 24, 2026·8 min read

Sharing a Lottie animation as a link someone can just open (2026)

A .json Lottie is not a page — hand it to a client and they get a wall of numbers. What it takes to turn one into something that opens in any browser, and why the answer is smaller than the tooling suggests.

September 23, 2026·5 min read

Send your own private link.

miinideck turns a single HTML file into an unguessable link with optional password and expiry. Default-private, never indexed.

Try it free →See pricing
miinideck

HTML files, finally as links — for AI builders, agencies, and consultants. Default-noindex, default-private, default-yours.

Product

  • Pricing
  • Use cases
  • Try it free

Resources

  • Blog
  • Free tools
  • Featured on
  • Report abuse

Legal

  • Privacy
  • Terms
Listed onmiinideck listed on Product Huntmiinideck listed on Faziermiinideck listed on TheSaaSDirmiinideck listed on AIToolHuntmiinideck listed on LaunchNest
© 2026 miinideckMade for people who don't want their work indexed.