This documentation is also published as Markdown for efficient machine reading: the whole site is indexed at /llms.txt, and every page has a clean Markdown copy at the same URL with .md appended. These are generated from the same source and cost far fewer tokens to read than this rendered HTML.

Skip to main content Skip to navigation
Guides

Host under a sub-path (base URL)

Serve a Pennington site from a non-root URL by passing `[baseUrl]` to the build and letting `BaseUrlHtmlRewriter` prefix every internal href, src, and action.

To serve under a sub-path, pass it as the first argument to build. BaseUrlHtmlRewriter prefixes every root-relative href, src, and action on the way out; the same RunOrBuildAsync call handles root and sub-path identically.

Before you begin

  • A working Pennington site that builds locally with dotnet run -- build (see Build a static site if not).
  • The sub-path the host will serve from — for example /docs for https://example.com/docs/ or /<repo> for a GitHub Pages project site.
  • Internal links authored as root-relative (/guides/first-page/). The rewriter only matches the leading /; protocol-relative (//cdn.example.com/x.js), absolute (https://…), hash (#section), and page-relative (./neighbor/) links pass through untouched.
  • The host is already configured to serve output/ at that sub-path (see Deploy to GitHub Pages or Self-host behind Nginx or IIS).

For a working setup, see examples/SubPathDeployableExample.

Build with the prefix

Pass the sub-path as the --base-url flag. Include the leading slash and omit the trailing slash — the rewriter normalizes either way.

bash
dotnet run -- build --base-url=/docs

For the positional form and the rest of the argument grammar, see CLI and build arguments.

Reproduce the prefix from client-side code

When an island, Blazor component, or custom script builds URLs at runtime, read the prefix from document.body.dataset.baseUrl (stamped by the rewriter) instead of hard-coding /docs. The same output/ then runs under /docs in staging and / in preview with only a different --base-url.

javascript
const base = document.body.dataset.baseUrl ?? "";
const href = `${base}/guides/first-page/`;

Verify

  • Run dotnet run -- build --base-url=/docs and open output/index.html — every internal href, src, and action now starts with /docs/, and <body> carries data-base-url="/docs".
  • Serve the build so the prefix is part of the path. A static server roots at /, so place output/ inside a folder named for the prefix and serve the parent: mkdir -p site/docs && cp -r output/* site/docs/ && npx http-server site -p 5000. Open http://localhost:5000/docs/ — deep links like /docs/guides/first-page/ resolve and their in-page links stay under the prefix.
  • Re-run with no --base-url — the generated HTML reverts to root-relative paths with no data-base-url attribute, confirming the rewriter short-circuits when the prefix is empty or /.