Skip to content

Add absolute links via output.html.site-url#3139

Open
JesusPerez wants to merge 1 commit into
rust-lang:masterfrom
JesusPerez:site-url-absolute-links
Open

Add absolute links via output.html.site-url#3139
JesusPerez wants to merge 1 commit into
rust-lang:masterfrom
JesusPerez:site-url-absolute-links

Conversation

@JesusPerez

Copy link
Copy Markdown

Closes #1764.
Supersedes #1802 (a reimplementation of @joaofreires' approach on the current crates/ workspace layout, with the review feedback addressed).

Problem

When a book is hosted in a subdirectory (or any page is reached at a deeply nested path), mdBook's depth-relative links can resolve incorrectly. output.html.site-url already exists but only feeds the <base href> of the 404 page — ordinary navigation, sidebar, asset and in-content links are not affected. See #1764.

What this does

When output.html.site-url is set (e.g. https://example.com/docs/), every generated link is rooted at that URL so the book resolves correctly regardless of the page's own depth:

  • In-content links — root-relative ./path links in chapter bodies (and images / raw HTML) are anchored to site-url. Scheme links (https:, mailto:) and fragments are left untouched.
  • Page chrome + JS sidebarpath_to_root is set to site-url, so prev/next, the print link, search and the toc.js-injected sidebar all become absolute.
  • No-JS sidebar fallbacktoc.html (the iframe fallback) gets a <base href> of site-url; this is scoped to that render so it never leaks onto regular pages (which would break their page-relative content links).
  • Static assets — the {{resource}} helper honors the page's path_to_root, so hashed CSS/JS are emitted absolutely too.
  • Print pageprint.html chrome/assets are rooted at site-url, and cross-chapter references between chapters present on the page are still folded into intra-page #id anchors, keeping the consolidated page self-contained.

During mdbook serve the book is hosted at the local server root, so site-url is overridden to / (host-agnostic, resolves locally). This was previously silent; it now logs once, and a new --preserve-site-url flag keeps the configured value to preview the production absolute links locally.

Addressing the #1802 review

ehuss's review point on #1802 Status here
"This will need some tests to be added." 7 integration tests + 4 unit tests (see below).
"links on the print page aren't using the adjusted links." print.rs::rewrite_links is now site-url-aware; cross-refs fold to anchors, externals stay absolute.
"there are some merge conflicts." N/A — clean reimplementation on current master.
"HtmlConfig is public API → a new field is a semver break; use a wrapper." No longer applies: in the crates/ layout site_url is already a field on HtmlConfig. No public API fields were added; threading is via internal Option<&str> parameters. cargo semver-checks passes.
"use if let over match, Option<&str> over Option<&String>." Uses Option<&str> (as_deref()) and let-chains throughout.
"the new option needs to be in the TOML config docs." Updated (see Docs).

Implementation

All link handling lives in mdbook-html; no public API was added.

  • crates/mdbook-html/src/html/tree.rsfix_link/fix_html_link take site_url; root-relative ./ links are anchored. Threaded at the three call sites via self.options.config.site_url.as_deref().
  • crates/mdbook-html/src/html_handlebars/hbs_renderer.rspath_to_root = site_url per chapter (and the index.html variant); base_url = site_url scoped to the toc.html render; print page rooted at site_url.
  • crates/mdbook-html/src/html_handlebars/helpers/resources.rs{{resource}} honors @root/path_to_root with a fallback (byte-identical output when site-url is unset).
  • crates/mdbook-html/src/html/print.rsrender_print_page/rewrite_links take site_url; the site_url prefix is stripped and the remainder resolved as root-relative so in-print cross-refs still fold to #id.
  • src/cmd/serve.rs--preserve-site-url flag; the / override is now logged and opt-out.

Tests

  • tests/testsuite/rendering.rssite_url_* (7): content link rewriting, external links untouched, absolute path_to_root, absolute assets, <base href> only on toc.html, no <base href> leak onto chapter pages, opt-in behavior when unset, and the print page (chrome absolute + cross-refs folded to anchors).
  • crates/mdbook-html/src/html/tree.rsfix_link_tests (4): .md.html, schemes/fragments left alone, ./site-url, bare-relative links unchanged.
  • Fixture: tests/testsuite/rendering/site_url/.

cargo test --workspace, cargo clippy --all-targets and cargo fmt --check all pass.

Docs

  • guide/src/format/configuration/renderers.md — the site-url bullet now describes whole-site absolute links and the serve override.
  • guide/src/cli/serve.md--preserve-site-url section.

Notes

  • The root-relative convention is the explicit ./path form, matching the original patch's semantics; bare relative links keep their page-relative meaning.
  • A ./ link to the first chapter (which mdBook aliases READMEindex) stays an absolute link to its standalone page in the print output rather than folding to an anchor. This is a pre-existing README/index aliasing quirk, independent of this feature.
  • Relationship to canonical-site-url (Add canonical-site-url setting #2706): the two settings are complementary and do not overlap. site-url is the deployment location and, with this change, the root for every emitted link/asset so the book resolves under a sub-path. canonical-site-url only emits <link rel="canonical"> for SEO and does not affect link resolution. They touch different code paths and can land independently.
  • CHANGELOG.md intentionally left untouched (maintained at release time).

Supersedes #1802; builds on @joaofreires' original approach there.

Port of the 0.4.x site-url absolute-links patch to the 0.5 crates/ layout,
toward upstreaming as PR rust-lang#1802. When output.html.site-url is set, internal
links and assets are emitted as absolute URLs anchored at site-url, so the
book works under a sub-path (e.g. /cdcidao/) regardless of page depth.

- html/tree.rs: fix_link/fix_html_link rewrite ./ content, image and raw-HTML
  links to {site_url}...; schemes and fragments untouched
- html_handlebars/hbs_renderer.rs: path_to_root = site_url for normal and index
  pages; base_url = site_url only for the toc.html iframe (removed before the
  per-chapter clone so it cannot leak)
- html_handlebars/helpers/resources.rs: {{resource}} honors an explicit
  path_to_root from data (absolute assets) with stock fallback
- html/print.rs: print page honors site-url; internal cross-refs still fold to
  #anchors, non-chapter links keep absolute form
- cmd/serve.rs: --preserve-site-url flag; serve still forces site-url to / for
  local preview but logs the override
- tests/testsuite/rendering*: site_url fixture + tests (content, assets, print,
  no <base> leak, no-regression without site-url)
- guide: document the serve flag and the renderer behavior
@rustbot rustbot added the S-waiting-on-review Status: waiting on a review label Jun 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-review Status: waiting on a review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

site-url not respected in links

2 participants