pyssg is pre-1.0 and under active development - APIs, config, and themes may change.
pyssg.plugins.redirects

3 min read

pyssg.plugins.redirects

Redirects plugin.

Emits a tiny meta refresh HTML page for every old URL -> new URL rule a site declares, so links to retired paths keep resolving after a migration (/posts/ -> /, a renamed slug -> its new permalink, /about/ -> an external CV). Each rule materializes a virtual :class:~pyssg.core.node.Page with template=None, whose body is the redirect HTML emitted verbatim by the render plugin (no layout) -- the same "summarizer page" mechanism the sitemap/rss plugins use.

The rules are literal: the source is the exact old URL (/posts/ -> posts/index.html) and the target is used as-is, so an absolute path (/new/) or a full external URL (https://cv.example.com) both work. This plugin is therefore locale-agnostic by design -- a localized site lists the old localized URLs explicitly rather than having the engine partition them.

A rule is skipped when its source URL already belongs to a real page (a redirect must never overwrite live content); the remaining set is materialized deterministically (sorted by source, body derived purely from the rule and base_url), so two builds are byte-identical and an incremental rebuild reuses cached output whenever the rules are unchanged. Rules dropped between builds have their stale page -- and thus their output -- removed.

The algorithm lives in :class:RedirectsPlugin methods so a site can subclass it to change the redirect markup (e.g. add a delay or a styled body) or the page-id scheme without forking the plugin (see AGENTS.md on plugin design).

render_redirect_html(*, target: str, canonical: str) -> str

Build the meta refresh redirect document pointing at target.

target is the value used for the instant <meta http-equiv="refresh"> and the visible fallback link; canonical is the absolute URL advertised to crawlers via <link rel="canonical">. The page is marked noindex so the stub itself never competes with the destination in search results. Both URLs are attribute- and text-escaped, so an ampersand in a query string is safe.

class RedirectsPlugin

Built-in meta refresh redirect generator, one page per rule.

Customise by subclassing: override :meth:render_html for the redirect markup, :meth:page_id for the node-id scheme, or :meth:canonical_url for how a site-relative target is made absolute, while reusing the collision check and emit wiring. Pass the rules through the :func:redirects factory.

RedirectsPlugin.__init__(self, *, rules: Mapping[str, str] | None = None) -> None

RedirectsPlugin.apply(self, builder: Builder) -> None

RedirectsPlugin.page_id(self, source: str) -> str

Stable node id for the redirect page serving source.

RedirectsPlugin.canonical_url(self, target: str, base_url: str) -> str

Absolute URL advertised as canonical for a redirect to target.

An already-absolute target is returned unchanged; a site-relative target is joined onto base_url so crawlers see a fully-qualified destination.

RedirectsPlugin.render_html(self, source: str, target: str, base_url: str) -> str

Render the redirect document for one source -> target rule.

RedirectsPlugin.build(self, build: Build) -> None

(Re)materialize one redirect page per rule and drop stale ones.

Skips any rule whose source URL or target is empty, or whose source URL is already served by a real page (live content always wins). Iterates the rules sorted by source so the emitted set is order-independent.

build_redirects(build: Build, rules: Mapping[str, str] | None = None) -> None

Materialize the redirect pages. Thin wrapper around :meth:RedirectsPlugin.build.

redirects(rules: Mapping[str, str] | None = None) -> RedirectsPlugin

Factory used in pyssg.config.py.

rules maps each old URL to its replacement; see :class:RedirectsPlugin.