How sites are served
Every deploy is an immutable tree of files, served on https://<project>.slew.cloud (and any custom domains you attach). This page covers what the router does with a request so you can predict how your site behaves.
URLs and subdomains
Your project name is your subdomain: project my-site serves at my-site.slew.cloud. One label only — nested subdomains like a.b.slew.cloud don't resolve to a project. Temporary shares serve at their tmp-*.slew.cloud URL until they expire, at which point they stop resolving immediately.
Path resolution and SPA fallback
Requests map onto the files you uploaded, with sensible fallbacks:
| Request path | Files tried, in order |
|---|---|
/ |
index.html |
/blog/ |
blog/index.html, then index.html |
/blog/post (no extension) |
blog/post, then blog/post/index.html, then index.html |
/app.css (has extension) |
app.css only |
The final index.html fallback for extensionless paths means client-side routed SPAs work out of the box — deep links land on your app shell. Paths with extensions never fall back, so a missing asset is a real 404 rather than your index.html served as CSS.
Percent-encoded paths are decoded; anything containing .. segments, backslashes, NUL bytes, or invalid encoding is rejected.
Custom 404 pages
If your deployment contains a 404.html at its root, it is served (with status 404) whenever no file matches. Without one, visitors get slew's minimal branded 404.
Caching
Responses fall into two classes:
| Asset class | Cache-Control |
|---|---|
Content-hashed build artifacts — Vite/webpack-style name-h4sh123.ext filenames (never .html) |
public, max-age=31536000, immutable |
| Everything else, notably HTML | public, max-age=30, stale-while-revalidate=60 |
Hashed artifacts are immutable by construction (new content means a new URL), so they cache for a year. HTML stays on a 30-second TTL, and slew additionally purges the CDN edge when you deploy or roll back — so a deploy or rollback is visible worldwide within seconds, without you managing any cache invalidation.
404 responses are never cached.
Content types and headers
Content types are derived from file extensions — HTML, CSS, JS, JSON, XML and text (all charset=utf-8), common image formats (svg, png, jpg, gif, webp, avif, ico), fonts (woff, woff2, ttf, otf), plus pdf, wasm, mp4, webm, mp3, and source maps. Unknown extensions are served as application/octet-stream. Every file response carries X-Content-Type-Options: nosniff and Referrer-Policy: strict-origin-when-cross-origin.
Rollback semantics
Deployments are never mutated: each upload gets its own file tree, and "live" is a pointer to one of them. slew rollback <deployment-id> (or the dashboard's "Roll back to this", or POST …/activate) flips that pointer and purges cached HTML. Any deployment that ever went live can be re-activated.
Old deployments you no longer need can be deleted — slew deployments rm <id>, the dashboard's "Delete", or DELETE /projects/:name/deployments/:id. The live deployment is protected; deleting anything else permanently removes its files, so it can no longer be rolled back to.
Custom domains
Attach a domain with POST /projects/:name/domains (see the HTTP API reference). You get back a dns_target; CNAME your domain to it and TLS is provisioned for free once DNS resolves. A domain can be attached to only one project platform-wide. Re-adding the same domain is safe and retries the TLS setup — useful right after flipping DNS.
To see the certificate a domain is actually serving — issuer, expiry, whether it's valid — run slew domains cert <domain> or use the dashboard's "Check certificate". If issuance is still pending (or you want to re-trigger it), the dashboard's "Renew certificate" (or POST …/certificate/refresh) asks the CDN to issue again.
Limits
| Limit | Value |
|---|---|
| Upload (compressed tarball) | 100 MB |
| Files per deployment | 10,000 |
| Single file | 25 MB |
| Total uncompressed | 250 MB |
Symlinks and directories in an archive are skipped — only regular files are served.