Host under a sub-path (base URL)
Serve a Pennington site from a non-root URL by passing `[baseUrl]` to the build and letting `Base
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
/docsforhttps://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.
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.
const base = document.body.dataset.baseUrl ?? "";
const href = `${base}/guides/first-page/`;
Verify
- Run
dotnet run -- build --base-url=/docsand openoutput/index.html— every internalhref,src, andactionnow starts with/docs/, and<body>carriesdata-base-url="/docs". - Serve the build so the prefix is part of the path. A static server roots at
/, so placeoutput/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. Openhttp://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 nodata-base-urlattribute, confirming the rewriter short-circuits when the prefix is empty or/.
Related
- Reference: CLI and build arguments — the
build [baseUrl] [outputDirectory]surface this page drives. - Background: The response-processing pipeline — why base-URL rewriting runs at
Order => 30, after xref and locale rewriters. - Background: Dev mode and build mode share one code path — why the same rewriter runs identically in
dotnet runanddotnet run -- build.