# Routing (`routing.json`) (https://lazyfrog.dev/docs/guide/routing-routing-json)

{/* Generated by scripts/generate-docs.ts from services/gateway/src/guide.md. Do not edit. */}

The `routing` tool edits `/workspace/routing.json`, which the platform applies in front of the app
(no app code needed): redirects, rewrites (to another path or service), response headers, CORS,
password-protected paths (`auth`), `allow_ips`, `allow_countries` and `error_pages`. Sources are
paths with `:name` for one segment and `:name*` for the rest (`/blog/:slug`, `/docs/:path*`);
`:name*` matches none too, so `/admin/:path*` covers `/admin` itself: one rule, one password.
`set` validates the whole file; an invalid file edited with fs tools is reported and the previous
version stays in force. Prefer it over code for redirects and security headers; commit it. Use
`Referrer-Policy: strict-origin-when-cross-origin`: `no-referrer` makes browsers send
`Origin: null`, which breaks form actions' CSRF checks (Next.js, SvelteKit, Django, Rails…). Against framing, use
`Content-Security-Policy: frame-ancestors 'self' https://app.lazyfrog.dev` rather than
`X-Frame-Options: DENY`, which also hides the site from the owner's portal. For a page only the owner
uses, add an `auth` rule with a username and no password (`{"source": "/admin/:path*",
"username": "owner"}`): the owner chooses the password in the portal (Settings › Protected pages);
never choose one for them. Until they have, the page stays locked: tell them where to set it.

Check a rule with `routing_read` `check` (`path`, and `env` for the live site): it asks from outside
as a visitor with no password and says locked, open, redirected or refused. `http_get`,
`http_request` and `preview_screenshot` go straight to the app, so they can't show this. Check
before telling the owner a page is protected; it works before they have chosen a password. A
preview with its own password answers first: check that path on the live site after publishing.
