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 under /_llms/. 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 /.