A Self-Built Static Demo Publisher That Treats Releases as Pointer Switches
Teams that run frequent client demos often outgrow free static hosts but don't need a full CMS. A small, self-hosted system with explicit version switching, path-traversal hardening, and automatic expiry removes the operational anxiety of broken links and stale deployments without adding platform dependency.
Client demos that rely on third-party static hosts introduce friction: platform switching, ads, unclear security boundaries, and links lost in chat history. A self-built system solves this by keeping every project under one admin panel with explicit states — upload, publish, offline, expire, delete. The flow creates a project, uploads a ZIP, extracts it to a temp directory, runs security checks, generates a version directory, and only then confirms publication behind a random-token URL.
Upload and publish are deliberately separated. An upload creates a pending version; an admin confirms it before switching the live pointer. This prevents half-finished pages from reaching a client during a demo and leaves room for version comparison and rollback. On the backend, ZIP extraction is sandboxed with path-normalization checks that reject traversal attempts like `../../application.properties`, plus limits on archive size, extracted size, file count, and allowed extensions. An `index.html` must exist at the root or one subdirectory deep.
Published versions live in independent directories (`v1/`, `v2/`, `v3/`), and the database simply records which version is current. A publish action updates that relationship without touching existing files, so a failed upload never breaks the live demo. Nginx handles HTTPS termination and proxies requests to the backend, with a note that X-Accel-Redirect could offload large static-file transfers later. Expiry and offline logic runs on access checks and scheduled cleanup tasks, preventing the server from becoming a museum of forgotten uploads.
The two-step upload-then-publish pattern is underused in internal tools. It costs almost nothing to implement and prevents the most common demo disaster: a client seeing a broken page while the developer assumes the deploy succeeded.
Treating a publish action as a database pointer switch rather than a file copy makes rollback trivial and keeps the live version stable even when a new upload fails mid-extraction.
Path-traversal checks are often taught as a security afterthought, but here they are the central design constraint — the system's entire trust model rests on the assumption that ZIP contents are hostile.