301s editable in Studio, enforced at the edge on deploy.
A redirects table the site owner edits in Studio — old path, new path, 301 or 302 — without touching config files. On deploy the redirects are compiled into whatever your host enforces at the edge (Vercel config, Netlify _redirects, or the one-process server itself), so moved pages keep their link equity and nobody sees a 404.
Redirects are just a content type, so the generic Studio gives you the editing UI for free. A build-time step fetches the published redirects from the content API and writes them into the host's redirect format; publishing a redirect triggers the publish webhook, which triggers a redeploy.
You are adding managed redirects to a Prelo project.
1. **Content type.** In `cms.config.js`:
```js
redirect: {
label: "Redirects",
fields: {
from: { type: "text", required: true }, // path, must start with /
to: { type: "text", required: true }, // path or absolute URL
code: { type: "select", options: ["301", "302"], required: true },
note: { type: "text" },
},
studio: { columns: ["from", "to", "code"] },
},
```
A redirect is live when published, off when draft — that gives the owner an on/off switch with no extra field.
2. **Fetch helper.** A small server-side helper `getRedirects()`: `GET {CMS_URL}/api/redirect?per_page=200` (paginate), returning `{from, to, code}` for published entries. Validate each: `from` starts with `/`, `to` is a path or http(s) URL, and `from !== to`; skip and warn on invalid rows. Detect and warn on duplicate `from` values (first one wins).
3. **Enforcement — pick what matches the deployment** (ask the user or read the repo):
- **Vercel frontend:** generate the `redirects` array in `next.config` at build time (`async redirects()` calling `getRedirects()`), mapping `code` 301→`permanent: true`.
- **Netlify:** a prebuild script writing `_redirects` lines (`{from} {to} {code}`) into the publish dir.
- **One-process Prelo site (`web/site.js`):** check the redirect list at the top of `handle(url)` and return `{ status: 301/302, redirect: to }` if the site module supports it — otherwise emit an HTML meta-refresh page with the proper status. Cache the list in memory for 60 seconds; don't hit the db per request.
- **Caddy/nginx self-host:** a deploy-time script writing a `redirects.caddy` / map file that the main server config imports.
4. **Redeploy on change.** Set `webhooks: { onPublish: "<deploy-hook-url>" }` in `cms.config.js` for static hosts so publishing a redirect rebuilds. For the one-process/self-host paths this is unnecessary (60-second cache).
5. **Safety rails.** Never allow a redirect from `/` (would loop the homepage into whatever someone types), from `/studio`, `/api`, or `/uploads` paths, or a chain longer than one hop you can detect (if `to` equals another redirect's `from`, warn at build time). Enforce in the fetch helper, not just docs.
6. **Verify.** Publish a test redirect in Studio, deploy (or wait out the cache), `curl -I` the old path and confirm the status code and `Location` header. Unpublish and confirm it stops.The owner manages redirects entirely in Studio: add a row when a page moves, publish it, done. Draft = disabled. The note field is theirs for "why does this exist" archaeology.
Sitemap, robots.txt and canonical tags generated from your pages automatically.
Contact form with spam protection and email notifications, plus a submissions inbox in Studio.
Filterable gallery with lightbox, fed straight from the media library.