Page aliases

A page may declare aliases: in its front matter - old or alternate site-local URLs it should also answer to. Requests to an alias redirect to the page's canonical URL: 301 Moved Permanently by default, or 302 Found for entries under aliases_temp: (SM134 and follow-ups).

Syntax

---
title: About us
aliases:
  - /old-about
  - /company/about
aliases_temp:
  - /about-2026
---

Both keys accept a YAML block list (as above) or an inline list (aliases: [/old-about, /company/about]). Only site-local absolute paths are kept: each entry must start with /; protocol-relative //… forms, .. sequences and control characters are dropped. If the same path appears under both keys on one page, the aliases_temp: entry wins (302).

The registry

Aliases are collected into a small map, lazysite/aliases.json, maintained automatically as pages are saved, deleted, moved and copied - across the manager, WebDAV MOVE/COPY and MCP writes alike, so a rename re-keys the map without waiting for the next save. A page cannot alias itself, and an alias claimed by two pages goes to the last writer, with a warning in the log.

The Files page shows the current map in a read-only Aliases card (alias, target, and a 301/302 badge), backed by the aliases-list control-API action (token clients: manage_content).

Resolution order

The processor consults the map only on the no-source-found path: a real page - including a cached one - always takes precedence over an alias. An alias hit answers with the redirect status, a Location header and a minimal HTML body linking the canonical URL.

Safety

The redirect target is always the declaring page's own canonical URL, derived from its file path - never an author-supplied value - so an alias can only redirect to the page that declared it, and the mechanism cannot be used as an open redirect to an external site. As defence in depth, the processor strips CR/LF from the Location header, HTML-escapes the body link, and refuses any map entry that is other than a clean site-local path.

Notes

  • Old maps keep working: a plain-string entry reads as a 301, the object form { "target": …, "code": 302 } carries a 302, and any unknown code is treated as 301
  • Restoring a version through Content history re-indexes aliases, as does a Remote sync pull