SlowDen

A full-folder deploy deletes what you leave out

By Liza · SlowDen · Published 19 August 2026

A drag-and-drop deploy is not a sync. It is a replacement. Whatever is in the folder becomes the site, and whatever is not in the folder stops existing. That sounds obvious written down, and it is still the reason a single drop can quietly destroy two working games and 111 working redirects.

This is what we found building ours, with the numbers we actually measured rather than the ones we assumed.

The build folder is not the site

Our generator writes pages. It does not delete them. That is a deliberate and sensible choice — a generator that prunes is a generator that can eat your work — but it means the output directory is cumulative. It holds every page the site has ever had.

When we counted, the output folder contained 111 directories for games that are no longer in our catalogue. Those games were retired months ago. Each of their URLs now has a 301 redirect pointing at the relevant category page, and none of them is in the sitemap. The redirects work.

The part that bites: a static host serves an existing file in preference to a redirect rule. Ship those 111 directories and you do not just republish 111 dead games — you shadow the 111 redirects that were doing their job. The rules stay in the config, still look correct, and stop firing.

So the bundle cannot be a copy of the build folder. It has to be assembled from an explicit allowlist: the slugs actually in the catalogue today, plus the category pages, plus the hand-maintained pages, plus the root files. Ours came to 200 game pages, 12 category pages, 5 standalone pages and 14 root files. Everything else was left out on purpose.

The files that are not in your repo

Two of our games are self-hosted rather than embedded. Their code lives in the project; their assets do not. Images, audio and sprite sheets had never been committed — they existed only on the live site.

Which creates the trap. Build a bundle without them, drag it, and the deploy does exactly what you told it to: it deletes them. The game keeps its page and stops loading.

The assets were still recoverable, because the live site was serving them. But recovering them means knowing every filename, and a game does not come with a manifest.

Listing every asset a game loads, without its source

The game is a single bundled JavaScript file, 1.76 MB. Every asset it loads is referenced somewhere inside it. So we pulled the bundle from the live site and searched it for string literals that look like asset paths — anything ending in an image, audio or data extension.

That returned 104 unique paths. Which raises the only question that matters: is 104 all of them?

The check that makes the list trustworthy

A regular expression finds literal strings. It cannot find a path the code builds at runtime — "assets/level-" + n + "/bg.png" would be invisible to it, and a bundle full of those would give a confident, badly incomplete answer.

So we searched for the construction itself: template literals containing assets/, and string concatenation next to it. Both returned zero. This bundle builds no paths dynamically, which is what upgrades the list from plausible to complete.

Then we asked the live site for each of the 104. 103 returned 200. The single miss was file.json — a generic string from a library, not one of our assets.

And one more check, which is the one people skip. We requested a filename we invented: assets/this-does-not-exist-xyzzy.png. It returned 404.

Without that, "103 files returned 200" means nothing. A host with a catch-all would return 200 for everything, and we would have congratulated ourselves on a list of files that do not exist. A checker that cannot fail is not a checker.

The number in our own notes was wrong

Our documentation said this game was "~205 MB". Measured across the 103 real files, it is 142.5 MB. Nobody had ever added it up; the figure was written down once and then quoted for months.

The same notes said the other self-hosted game needed "js.js, style.css, assets". It has no assets directory at all — three files, and its 3D library comes from a CDN.

Neither error was dangerous on its own. Both would have shaped how someone planned the work.

Verify the bundle before you trust it

The whole point is that the failure mode is silent: a bad bundle deploys perfectly and simply has less in it. So the bundle gets checked before it goes anywhere, not after:

That last one is the most useful check we have. A sitemap listing URLs the bundle does not contain is a promise to a crawler that the deploy is about to break.

What we would tell someone starting this

Three things, in the order they cost us time:

None of this makes a deploy exciting. It makes it boring, which is the goal.

Questions

Does this apply to git-based deploys too?

Less sharply. A git-connected build deploys what the build produces, so the risk shifts to whatever your build step forgets rather than to what you dragged. The specific trap here — a static file shadowing a redirect rule that is still in your config — applies either way, because it is a serving-order behaviour, not a deploy-method one.

Why not just commit the game assets?

You should, and that is the real fix. This describes how to recover when they are not committed yet, which is the situation a lot of small projects are actually in.

Is parsing a bundle for asset paths reliable?

Only with the dynamic-path check. If the bundle builds paths at runtime, a literal-string search returns a confident partial list and you will not know it is partial. Check for the construction first; if you find any, this approach does not work and you need the source or a network capture of a full playthrough.