We froze a 2007 Joomla site without freezing its attack surface
A reproducible migration log: 442 discovered routes, a script-free archive, explicit redirects, and the evidence behind every decision.

The old Smartycode ran Joomla 1.0.12 on PHP 5.4. It still held useful articles, incoming links, and fifteen years of web history. It also held exactly the kind of executable surface that should not survive as a museum piece.
The experiment was simple: can we preserve the public record while deleting the runtime?
Claim
A legacy content site can remain faithfully browsable without keeping its CMS, database, JavaScript, or PHP reachable from the internet.
Method
We crawled the pinned live origin, normalized its historical aliases, fetched every same-origin dependency, removed executable behavior, rewrote internal links, and generated route policy from one inventory. The build fails if an output target is broad, if redirects and gone routes overlap, or if archived HTML contains a script.
The public inventory records the source URL, HTTP result, MIME type, disposition, target, title, provenance, and local file for each item. The archive itself runs under a content security policy with script-src 'none'.
Evidence
| Measurement | Result |
|---|---|
| Discovered routes and assets | 442 |
| Preserved pages | 92 |
| Preserved assets | 305 |
| Explicit redirects | 8 canonical route decisions |
| Explicitly retired responses | 37 |
| Executable scripts allowed | 0 |
Download the complete route inventory or run the production verifier. Both are the artifacts used by this page, not simplified examples.
What failed first
The first archive build looked fine locally and then broke at the edge. Cloudflare injected its Web Analytics script into otherwise script-free HTML, violating the archive CSP. The correct fix was not to weaken the CSP. We added a hostname-specific edge rule that disables injection for archive.smartycode.com while leaving privacy-conscious analytics available to the new site.
A second failure came from a generic rate-limit rule for extensionless requests. Normal archive navigation looked like abuse because old Joomla URLs rarely carry file extensions. The archive hostname now has an explicit exclusion.
Verdict
Verified. The old site is browsable at archive.smartycode.com, the origin runtime remains available only as a short-lived rollback source, and every known public route has an explicit disposition.
The reusable lesson is not “use a crawler.” It is to make preservation a policy problem with an exhaustive manifest. A screenshot is nostalgia. A route inventory, deterministic build, restrictive CSP, and production checks are an archive.
Reproduce it
- Download the inventory and verifier above.
- Run
sh lab-001-reproduce.shon macOS or Linux withcurland Python 3. - Inspect the JSON output. A non-zero exit means the live archive no longer matches this experiment’s acceptance criteria.
The next experiment will measure the new site’s production Core Web Vitals against the Joomla origin and publish the raw Lighthouse reports.