Preview link broken? It's almost always the paths.
It worked on your machine and died on the link. That is not the host being flaky — a short list of culprits covers nearly every broken preview, each with a fix you can check in a minute.
Diagnose the symptom first
Don't touch code yet: open the broken link, launch your browser's dev tools, and inspect the network tab. Every red row names a file the drop simply doesn't have — and that filename, letter for letter, is the whole diagnosis. Everything below matches your symptom to its cause.
Two facts explain almost every issue. A drop serves strictly what you uploaded at the root of its own subdomain, with case-sensitive paths — about.html and About.html are two completely different files here. No rewrite rules exist: no SPA fallback, no framework magic. What you zipped is precisely what gets served.
The four usual suspects
1 — Blank screen: you shipped the source, not the build
Before: zip -r site.zip my-project/ # src/, package.json, node_modules
After: npm run build
cd dist && zip -qr ../site.zip . # the build output is the siteUpload the folder your build writes — dist/, build/, out/, public/ or _site/, depending on the tool. The source project doesn't contain a working website.
2 — Assets 404 after a framework build — baked-in base paths
Before: <link href="/my-app/assets/index-B2xR.css"> # built with base "/my-app/"
After: leave the base at its default "/" — a drop mounts your files at its own rootVite base, Vue publicPath, CRA homepage, Hugo baseURL — keep them default for previews. Each drop is its own origin; there is no parent subpath to mount to.
3 — A single image 404s — case mismatch
Before: <img src="Logo.png"> with logo.png on disk
After: match the filename exactly, character for charactermacOS and Windows filesystems tolerate case differences; our serving layer does not. The bug lurks locally and only bites once the link goes live.
4 — Root page works, deep links 404 — SPA routing
Before: sharing https://<slug>.shipped.run/dashboard/settings (history-mode routing)
After: hash routing (#/dashboard/settings) works everywhere — or prerender real files per routeDrops serve static files directly, so a history-mode deep link has no real file to hit. The entry page still works — share that, swap the router to hash mode, or prerender each route (output: export, adapter-static, nuxi generate).
Why a key matters here
Debugging goes faster against a stable URL. Anonymous drops are immutable, meaning every fix attempt demands a brand new link to re-send. A named drop on Pro redeploys the exact same address in place — whoever reviews the fix never needs a new URL.
Inside CI, the GitHub Action's name: input pulls the exact same trick per pull request — each push updates the preview at one stable address.
The 60-second triage run
Check
Load the URL in a private window. If it renders cleanly, your own browser cached an older deploy — not a fix, but a solid clue.Check
Dev tools, Network tab, reload: every red row is a file your drop simply lacks. Compare against your build output letter-for-letter, casing included.Check
Unzip the uploaded archive into an empty folder and open index.html. Broken there as well? The build broke it, not the upload.Check
Spin up npx serve dist in the exact folder you uploaded. Works on localhost but dies online? The difference is base path or case — nothing else differs.Questions
"It works on my machine" — why not here?
Because your machine forgives two things a real server doesn't. Opening index.html over file:// papers over missing pieces, and a case-insensitive filesystem lets Logo.png answer for logo.png. Serve the folder over HTTP locally and most mysteries reproduce at home.
It runs in vite preview or astro preview — what changed?
Both serve your build almost identically to a drop — decent rehearsals, until a custom base path tailored for your production domain breaks things. Stick to the default base and the same folder works cleanly in both environments.
Fonts or icons missing while everything else loads?
Same culprits, narrower blast radius. Inspect the exact 404 in your network tab to verify casing and path resolution. Pulling assets from a CDN? Check the console: cross-origin font blocks show up as warnings, not always red network rows.
My framework needs a server — can the drop run it?
No. Drops are strictly static: zero Node, zero PHP, no SSR daemon, no request runtime. Prerender every route into flat files — output: export for Next.js, adapter-static for SvelteKit, nuxi generate for Nuxt — and ship those.
Still stuck?
Drop the folder once more and run through the checklist symptom by symptom. Or pass it to an agent: connect the MCP server, and Claude Code or Cursor can deploy, inspect, and troubleshoot with you — setup takes a minute at /guides.
Ship the fixed build
Fix the path, zip the build output, upload again — the new link is live before that old tab finishes reloading.