Skip to content

Add absolute links support#1802

Closed
joaofreires wants to merge 7 commits into
rust-lang:masterfrom
joaofreires:add_absolute_links_support
Closed

Add absolute links support#1802
joaofreires wants to merge 7 commits into
rust-lang:masterfrom
joaofreires:add_absolute_links_support

Conversation

@joaofreires

@joaofreires joaofreires commented May 7, 2022

Copy link
Copy Markdown

Prepend the site-url in links with absolute path.

Problem:

If we host in the book in a sub page we lost the references for the relative root. We already have the site-url in configs, but we do not use it. If we want to link a page in the root of the book, we need to prepend a lot of ../ to reach the actual root, and if we move pages these relative refs could lead us to dead links.

Proposal

Add a new flag use-site-url-as-root in the HtmlConfig.
The changes should have backward compatibility, we'll need to enable the use-site-url-as-root flag in [output.html] section inside the book.toml file to use the new feature.

TODO:

  • check if other type of links were affected by the changes

Closes: #1764

@watsom27

watsom27 commented Sep 6, 2022

Copy link
Copy Markdown

Hey,

Is there any update on this one? This fixes the exact issue im having. Would be awesome if we could get this merged/looked at again.

Thanks

@joaofreires

Copy link
Copy Markdown
Author

I can fix the merge conflict. Though, I still need someone to review/merge it.

@watsom27

watsom27 commented Sep 7, 2022

Copy link
Copy Markdown

You'd be an actual hero if you did, thank you.

I replied on the issue thread too so hopefully someone notices.

@schungx

schungx commented Sep 8, 2022

Copy link
Copy Markdown

You'd be an actual hero if you did, thank you.

Yes, this is long overdue. I currently have to resort to a templating engine to handle absolute paths. I use absolute paths almost exclusively as they don't require massive editing when I move a chapter up/down folder levels.

@joaofreires joaofreires force-pushed the add_absolute_links_support branch from addf00d to d9c5f10 Compare September 9, 2022 23:35
@watsom27

Copy link
Copy Markdown

Bump @ehuss

@ehuss ehuss left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR!

I think it is a little unfortunate to need to add a new config setting for this, but I don't see much of a way around it.

This will need some tests to be added.

It looks like links on the print page aren't using the adjusted links.

It also looks like there are some merge conflicts.

Comment thread src/utils/mod.rs Outdated
Comment on lines +206 to +252
pub fn render_markdown_with_path(
text: &str,
curly_quotes: bool,
path: Option<&Path>,
abs_url: Option<&String>,
) -> String {

@ehuss ehuss Oct 14, 2022

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is a public API, it can't be changed without a semver break. I think as a workaround, there will need to be another function added which takes the new parameter, and render_markdown_with_path can call it with None. We can then drop the new (or old) function in the future.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, I also left a comment to merge those functions in the future.

Comment thread src/utils/mod.rs Outdated
Comment on lines +146 to +147
}
.into();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what the .into() is here for, but I don't think it is necessary?

Suggested change
}
.into();
};

Comment thread src/utils/mod.rs Outdated
Comment on lines +143 to +148
fixed_link = match abs_url {
Some(abs_url) => format!("{}{}", abs_url.trim_end_matches('/'), &fixed_link),
None => fixed_link,
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a minor opinion on style, I think it might be a little clearer to use an if let instead of a match. It is 1 less line and makes it a little clearer that there is "no change" when the value is None.

Suggested change
fixed_link = match abs_url {
Some(abs_url) => format!("{}{}", abs_url.trim_end_matches('/'), &fixed_link),
None => fixed_link,
}
if let Some(abs_url) = abs_url {
fixed_link = format!("{}{}", abs_url.trim_end_matches('/'), &fixed_link);
}

Comment thread src/config.rs
Comment on lines +519 to +586
/// Prepend the `site_url` in links with absolute path.
pub use_site_url_as_root: bool,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, so technically adding this is a breaking change since HtmlConfig is in the public API. However, we have been adding new fields to this struct for a while now, and nobody has complained. That is something we should definitely fix in the future, but for now I guess we can let it slide. 😦

navigation links and script/css imports in the 404 file work correctly, even when accessing
urls in subdirectories. Defaults to `/`. If `site-url` is set,
make sure to use document relative links for your assets, meaning they should not start with `/`.
- **use-site-url-as-root:** Prepend the `site_url` in links with absolute path.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also add this to the TOML summary up above?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, done,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

links with absolute path

As a user, I would wonder "what links are those"? It could be

  • links generated by templates, such as those referencing style sheets and JavaScript code
  • links generated between pages of the book
  • links I insert using Markdown syntax

@ehuss ehuss added the S-waiting-on-author Status: The marked PR is awaiting some action (such as code changes) from the PR author. label Oct 14, 2022
Comment thread src/utils/mod.rs Outdated
Comment on lines +129 to +150
fixed_link.push_str(&caps["link"].trim_start_matches('/'));
fixed_link.push_str(".html");
if let Some(anchor) = caps.name("anchor") {
fixed_link.push_str(anchor.as_str());
}
} else if !fixed_link.is_empty() {
// prevent links with double slashes
fixed_link.push_str(&dest.trim_start_matches('/'));
} else {
fixed_link.push_str(&dest);
};
return CowStr::from(fixed_link);
if dest.starts_with('/') || path.is_some() {
if let Some(abs_url) = abs_url {
fixed_link = format!(
"{}/{}",
abs_url.trim_end_matches('/'),
&fixed_link.trim_start_matches('/')
);
}
}
return CowStr::from(format!("{}", fixed_link));

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I needed to add changes to this function because of leading double slashes when the path and abs_url var are defined. I've added some tests to make sure it only affects those cases.

Comment thread src/utils/mod.rs Outdated
text: &str,
curly_quotes: bool,
path: Option<&Path>,
abs_url: Option<&String>,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The &String type should normally not be used — &str is more flexible for the callers since they can pass in both string literals (that would simplify the tests below) and borrowed owned strings.

@mgeisler

mgeisler commented Feb 8, 2023

Copy link
Copy Markdown
Contributor

I think it is a little unfortunate to need to add a new config setting for this, but I don't see much of a way around it.

Would it not be better to add a syntax to let links use the path_to_root setting instead? That would be fully backwards compatible and avoid the need for a new config flag.

I'm thinking of something like

To back to [the root]($path_to_root/index.md)

The $path_to_root part would be a relative link, so it works even if site-url is not set. The syntax could also be {{ path_to_root }} if you let handlebars render it.

@JesusPerez

JesusPerez commented May 18, 2023

Copy link
Copy Markdown

site-url value in book.toml did not work for me as far as I tried with mdbook v0.4.29

  • chapter-item are not changed for {{toc}}
  • **<base href ** only works for 404.page
  • {{ path_to_root }} for index.hbs has relative dots in hrefs

I can not rely on base href to solve all links and references use cases, dots in paths does not always help.
SUMMARY.md has to use relative paths

To solve this I made some changes in:

Changes can be seen in this fork (updated to v0.4.31) :

master...JesusPerez:mdBook:master

Obviously to complete the absolute paths in the pages I use a variable {{baseurl}} as a prefix of the href and src links.
How to use it is described here: how to use absolute links in content

[Introduction]({{urlbase}}introduction.md)
![Image]({{urlbase}}image.jpg)

@KFearsoff KFearsoff left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code looks good to me, and the tests run, but unfortunately I wasn't able to actually confirm on the test_book the repo has that this PR fixes the issue. Is there some issue introduced in the latest commits that breaks this?

@calvinbrewer

Copy link
Copy Markdown

The code looks good to me, and the tests run, but unfortunately I wasn't able to actually confirm on the test_book the repo has that this PR fixes the issue. Is there some issue introduced in the latest commits that breaks this?

Confirmed here as well. Not working here: https://github.com/calvinbrewer/mdBook/tree/add_absolute_links_support with the configuration added as reference

@joaofreires

Copy link
Copy Markdown
Author

The code looks good to me, and the tests run, but unfortunately I wasn't able to actually confirm on the test_book the repo has that this PR fixes the issue. Is there some issue introduced in the latest commits that breaks this?

Confirmed here as well. Not working here: https://github.com/calvinbrewer/mdBook/tree/add_absolute_links_support with the configuration added as reference

Sorry If I'm saying something wrong, but I opened this PR more than a year ago.
I did not include any examples in the test_book. Could you please clarify what you mean by 'Not working here'? What specific aspect are you testing? The proposed solution involves adding the site-url as a root in absolute paths. Therefore, in your configuration example, it would be /docs/. If you create a Markdown link in your test_book, such as [Prefix](/prefix.md) (note it starts with /), it should correctly generate an HTML a element with the href attribute set to /docs/prefix.html.

@calvinbrewer

calvinbrewer commented Dec 8, 2023

Copy link
Copy Markdown

Sorry If I'm saying something wrong, but I opened this PR more than a year ago. I did not include any examples in the test_book. Could you please clarify what you mean by 'Not working here'? What specific aspect are you testing? The proposed solution involves adding the site-url as a root in absolute paths. Therefore, in your configuration example, it would be /docs/. If you create a Markdown link in your test_book, such as [Prefix](/prefix.md) (note it starts with /), it should correctly generate an HTML a element with the href attribute set to /docs/prefix.html.

I've added an example in the test_book test_book/src/individual/link_hr.md that links to the prefix.md here. This does not seem to have the site-url added to the rendered a href.

In addition, from a usability perspective, I'd assume the left nav items to also include the site-url as a prefix. I may be mistaken on the intent of the original absolute link support. Ideally, the entire mdBook application would live under a main domain as a micro application e.g. https://example.com/docs

@joaofreires

Copy link
Copy Markdown
Author

I've added an example in the test_book test_book/src/individual/link_hr.md that links to the prefix.md here. This does not seem to have the site-url added to the rendered a href.

I have successfully built your branch, and the HTML output from your example seems to be producing the expected results. In test_book/book/individual/link_hr.html, the following segment is generated for your example:

....
<a href="/docs/prefix.html">This</a> should link to prefix.md.
....

IMO, this outcome aligns with our assumptions. Could you please share your build results? Maybe I missed something.

In addition, from a usability perspective, I'd assume the left nav items to also include the site-url as a prefix. I may be mistaken on the intent of the original absolute link support. Ideally, the entire mdBook application would live under a main domain as a micro application e.g. https://example.com/docs

My intention with this particular changes was not to address all those cases. Regarding the navigation links, they are generated based on SUMMARY.md, which, as per the mdBook documentation, requires all links to be relative.

@KFearsoff

Copy link
Copy Markdown
Contributor

Therefore, in your configuration example, it would be /docs/. If you create a Markdown link in your test_book, such as [Prefix](/prefix.md) (note it starts with /), it should correctly generate an HTML a element with the href attribute set to /docs/prefix.html.

Hmm. I've tried doing this by myself, and after you wrote a response I checked again with the exact inputs you've provided. Here's a patch that I'm got for testing:

diff --git a/test_book/book.toml b/test_book/book.toml
index a305007..91e8c9d 100644
--- a/test_book/book.toml
+++ b/test_book/book.toml
@@ -9,6 +9,8 @@ edition = "2018"
 
 [output.html]
 mathjax-support = true
+site-url = "/docs/"
+use-site-url-as-root = true
 
 [output.html.playground]
 editable = true
diff --git a/test_book/src/individual/README.md b/test_book/src/individual/README.md
index 67a1e9d..fceb065 100644
--- a/test_book/src/individual/README.md
+++ b/test_book/src/individual/README.md
@@ -2,6 +2,8 @@
 
 This contains following tags:
 
+[Prefix](/prefix.md)
+
 - Headings
 - Paragraphs
 - Line breaks

I then run cargo run -- serve --hostname 127.0.0.1. My expectation is that at http://127.0.0.1:3000/individual/index.html, I should see a link to http://127.0.0.1:3000/docs/prefix.html, but I'm seeing a link to http://127.0.0.1:3000/prefix.html. Am I missing something?

@joaofreires

Copy link
Copy Markdown
Author

@KFearsoff to view the results, it is necessary to execute the command cargo run -- build. This is because the server mode overrides the site-url configuration to "/" for development purposes, I presume.

You can check it here:

book.config.set("output.html.site-url", "/").unwrap();

@KFearsoff

Copy link
Copy Markdown
Contributor

@KFearsoff to view the results, it is necessary to execute the command cargo run -- build. This is because the server mode overrides the site-url configuration to "/" for development purposes, I presume.

Oh. Ohhh. Yeah, I can confirm this works.

This is... pretty unfortunate, though. I think serve overriding the setting is extremely unintuitive and does more harm than good, especially with this PR. Do you mind seeing if it's possible to remove, and can we afford making this breaking change? Because I'd really hate it if we can't.

@joaofreires

Copy link
Copy Markdown
Author

@KFearsoff, I believe this constitutes a reasonable usage. The serve mode is designed exclusively for development purposes and is tasked solely with hosting the book files. I agree that enhanced documentation would be beneficial in this context though.

Another aspect to consider carefully is the content of site-url. It's important to note that this is not limited to pathnames; it can include absolute URLs complete with hostnames and schemas. This flexibility could potentially lead to further complications when operating on a local server if we want to include the site-url as is.

It would be good to have more feedbacks on it before any change.

@JesusPerez

JesusPerez commented Jan 9, 2024

Copy link
Copy Markdown

Just in case if it helps I updated my mdBook with absolute links via site-url based in current mdBook master branch (v0.4.40 f480534)

I only fixed a couple of files, maybe it is not enough but it seems to work for me.

You can clone it, build and try following how-to-use-it notes with sitefix-book as example.

For instance I have tested absolute URL with hostnames and schemas and it works !
@joaofreires I use to keep absolute path in most cases and leave the rest for web service resolution (server and browsers) in some circunstances "<base href=" in index.hbs can also help.

I have to deal with multi-books services with auth, for me it is critical to support absolute links.

Let me know if I can help on this.

@RyanSquared

Copy link
Copy Markdown

What needs to be done for this feature to be merged?

@rustbot

rustbot commented Apr 30, 2025

Copy link
Copy Markdown
Collaborator

☔ The latest upstream changes (possibly #2681) made this pull request unmergeable. Please resolve the merge conflicts.

Refactors multiple functions to replace `Option<&String>` with `Option<&str>`
for improved readability and adherence to Rust best practices.

Simplifies `render_markdown_with_path` to remove unused `abs_url` parameter
and introduces `render_markdown_with_abs_path` for enhanced link rendering
flexibility.

Fixes test cases to align with the updated parameter types.
@joaofreires joaofreires force-pushed the add_absolute_links_support branch from 2336e6a to 7c42e4a Compare July 1, 2025 22:52
@schungx

schungx commented Aug 18, 2025

Copy link
Copy Markdown

Three years and I'm still relying on a templating engine to put in the correct root for links.

Will this feature ever get into production?

@watsom27

watsom27 commented Sep 2, 2025

Copy link
Copy Markdown

Three years and I'm still relying on a templating engine to put in the correct root for links.

Will this feature ever get into production?

Seconded

JesusPerez pushed a commit to JesusPerez/mdBook that referenced this pull request Jun 18, 2026
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
@JesusPerez

Copy link
Copy Markdown

This problem is still live — site-url only affects the 404 page, so books hosted in a subdirectory still get incorrect navigation, sidebar, asset and in-content links (the original report is #1764).

Since this PR has been waiting on the author for a while and the codebase has since moved to the crates/ workspace layout, I've opened a fresh PR that reimplements your approach on current master and addresses the review here:

It covers each point from your review on this PR:

  • Tests added (integration + unit).
  • Print page links now use the adjusted/absolute links and still fold cross-references to in-page anchors.
  • No merge conflicts (clean reimplementation).
  • No public-API/semver break — site_url is already a field on HtmlConfig in the new layout, so nothing public was added (cargo semver-checks passes).
  • if let / Option<&str> style applied.
  • TOML config docs updated.

The original approach here is yours — the new PR is marked as superseding this one and credits it. Happy to fold in any feedback there.

cc @ehuss

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-author Status: The marked PR is awaiting some action (such as code changes) from the PR author.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

site-url not respected in links

10 participants