sphinx-notfound-page
Generates a Sphinx 404 page with absolute URLs so navigation, images, and links keep working no matter which path depth triggered the error.
Repository Health
Technical Analysis
sphinx-notfound-page is a Sphinx extension maintained by Read the Docs that builds a custom 404.html page with all internal links, images, and static assets rewritten to absolute URLs. A 404 page can be served from any directory depth on a site, so the relative links that work fine on normal pages break once rendered at an arbitrary path — this extension solves that by hooking into Sphinx’s page-rendering pipeline and forcing every URL to resolve against a configurable prefix.
It ships as a drop-in extension: add notfound.extension to the extensions list in conf.py and it generates the 404 page automatically during the build, including projects hosted on Read the Docs, where the URL prefix is derived automatically from the READTHEDOCS_CANONICAL_URL environment variable.
What You Get
- Automatic 404.html page generation during the Sphinx build, no manual template needed
- Absolute-URL rewriting for links, images, and toctree entries so the page works at any URL depth
- Read the Docs integration that derives the URL prefix from READTHEDOCS_CANONICAL_URL automatically
- Configurable notfound_context, notfound_template, and notfound_pagename settings for full page customization
Common Use Cases
- Documentation sites hosted on Read the Docs that need a working custom 404 page
- Multi-version or multi-language doc projects where a 404 page must resolve links correctly from any version/language subpath
- Static Sphinx sites deployed behind a CDN or custom domain needing hardcoded absolute URLs on error pages
- Projects wanting a branded not-found page with working sidebar navigation instead of Sphinx’s default broken-link 404
Under The Hood
Architecture
The extension is a single Sphinx plugin registered via notfound/extension.py’s setup() function, which connects four event handlers into Sphinx’s build lifecycle: config-inited validates the notfound_urls_prefix setting, html-collect-pages emits the 404 page spec (skipped if the project already has its own 404.rst or is building an embedded format like epub), html-page-context (at priority 400, ahead of Sphinx’s own resource-path resolution) overrides the pathto, toctree, css_tag, and js_tag template helpers so every generated URL on the 404 page is absolute, and doctree-resolved rewrites image URIs via the shared replace_uris() helper in notfound/utils.py. An OrphanMetadataCollector environment collector marks the generated page orphan and nosearch so Sphinx doesn’t warn about it being outside any toctree. The design is intentionally narrow — it only touches rendering for the single configured notfound_pagename, leaving the rest of the build untouched.
Tech Stack
Pure Python, depending only on sphinx>=5 (supporting Sphinx 5 through 9 per its tox matrix) and docutils (transitively, via Sphinx). It uses Sphinx’s public extension API (add_config_value, app.connect, add_env_collector) exclusively, with version-gated branches (sphinx.version_info >= (7, 2) / (7, 3)) to handle breaking changes across Sphinx releases such as the introduction of _CascadingStyleSheet/_JavaScript asset objects and the global_toctree_for_doc API. Packaging uses flit_core with pyproject.toml, and the module is published as notfound (import name) versus sphinx-notfound-page (PyPI distribution name).
Code Quality
Tests live under tests/ using pytest with the sphinx.testing fixtures (@pytest.mark.sphinx), building against fixture doc projects in tests/examples/ (default, 404rst, extension, parallel-build) and asserting on generated HTML output rather than mocking Sphinx internals. tox.ini runs the suite across a large matrix of Python (3.8–3.14) and Sphinx (5–9, latest, dev) versions via CircleCI, giving strong confidence the extension keeps working across the ecosystem it targets. Code carries substantial docstrings and inline comments explaining why (e.g. citing specific Sphinx GitHub line numbers being reimplemented), and shared logic (URI replacement) is factored into utils.py rather than duplicated.
What Makes It Unique
Rather than generating a static, standalone error page, it reuses the same Sphinx theme, sidebar, and toctree the rest of the documentation uses, but transparently swaps every relative reference to an absolute one — including replicating and monkey-patching Sphinx’s internal pathto/toctree/css_tag/js_tag template functions rather than post-processing the rendered HTML. That gives a themed, fully-navigable 404 page that looks identical to the rest of the docs, which is a materially different approach from writing a hand-rolled static 404.html.