About problem with canonifyurls

I have problem with canonifyurls , the rendering of markdown links are not the same if I use canonifyurls true or false.
There is not theme or other tem{{ .content }}plate used , only hugo installed , nothing other.
Problem occurs only when using a subdomain in baseURL
The subdomain disappear from link rendering if canonifyurls is set to false.
HUGO developpers says this is not a bug.
But I think markdown links must have the same rendering whatever canonifyurls is true or not.

You can check it f .Here is how :

1. hugo create new site check
2. adapt config.toml with baseurl = "http://example.org/hugo/
3. cd to check/content create a file this.md with following content

+++
+++

this

4 cd to layout, create _default directory
5 cd to _default
6 create file single.html with content
	`{{ .content }}`
7. Run hugo server
8. goto your site http://localhost:1313/hugo/this
9. Rollover "this" and check url ==> http://localhost:1313/this
10. Now modify config.toml by adding canonifyURLs = true
11. Rollover again(after saving/restarting) ==> http://locaalhost:1313/hugo/this

So it is ok with canonifyurls set to true , but some themes need canonifyurls set to false and all the markdown links are then bad with error 404.
Yes I can adapt each link by adding the sybdomain before , but it is not acceptable.

Somebody a solution ?

@id027102 Thank you. This is an excellent question, and you are not the first to have struggled with it.

I think there are a couple of topics here:

  1. What does the canonifyURLs site configuration setting do?
  2. How do I configure a site to be served from a subdirectory?

What does the canonifyURLs site configuration setting do?

When your markdown is initially converted to HTML, the rendering engine (Goldmark) converts your links to <a> elements, and images to <img> elements, following the CommonMark specification.

The rendering engine has no context for link conversion. It just follows a set of rules. It neither knows nor cares about:

  • The URL to your server root (https://example.org/)
  • The URL from which your Hugo-generated files will be served (https://example.org/blog/)
  • Whether or not a <base> element exists with the <head> element of a Hugo template
  • The baseURL Hugo configuration setting
  • The canonfiyURLs Hugo configuration setting
  • The relativeURLs Hugo configuration setting

Regardless of these settings, in the absence of a markdown render hook, the markdown renderer will always convert this:

[text](destination)
[text](/destination)
[text](./destination)
[text](../destination)

to:

<a href="destination">text</a>
<a href="/destination">text</a>
<a href="./destination">text</a>
<a href="../destination">text</a>

Try it.

When you ask Hugo to canonfiy URLs, it performs the operation after the markdown has been converted to HTML. Conceptually…

The canonify process performs a simple search and replace within the HTML generated by the renderer. It looks for things like href=/a or src=/b, and replaces the leading / (if there is one) with the baseURL in your site configuration.

Try it. Set canonifyURLs = true in your site configuration, then create a markdown page that looks like this:

+++
title = 'Test'
date = 2021-11-09T20:12:32-08:00
draft = false
+++
href=a

href=/a 

src=b 

src=/b

This is the published page:

image

Notice that:

  • The markdown did not contain any links.
  • The markdown renderer did not create any anchor elements.
  • The canonify process found some text in the rendered HTML that looked like a URL with a leading /, so it replaced the leading / with the baseURL from the the site configuration.

That’s what canonifyURLs does. It’s a lot like sed, or Ctrl+H in your favorite editor.

How do I configure a site to be served from a subdirectory?

If you are using a theme developed by a third party, it may be difficult or even impossible without modifying the theme. Why? Because some themes were not designed or coded with this configuration in mind.

An ideal, high quality theme would allow you to:

  • Serve the site from the server root or from a subdirectory
  • Enable or disable canonifyURLs
  • Enable or disable relativeURLs
  • Enable or disable uglyURLs
  • Navigate the published site without a web server (open /index.html from the file system)
  • Generate valid RSS feeds
  • Localize your site for multiple languages
  • See the images you have included in a page while using your markdown editor
  • Navigate the links you have included in a page while using your markdown editor

Additionally, the theme author must provide documentation explaining how to:

  • Configure your site for the supported scenarios
  • Construct your links and images in markdown

Are there any themes that meet all of these criteria? I don’t know. It’s not a trivial goal.

Method 1: Base Element

One approach to allowing a site to be served from the server root or a subdirectory is to include a <base> element within the <head> element of each template (or in baseof.html if used). That would look like:

<base href="{{ site.BaseURL }}">

From Mozilla…

The <base> HTML element specifies the base URL to use for all relative URLs in a document. There can be only one <base> element in a document.

Also from Mozilla…

Example: /en-US/docs/Learn

This is the most common use case for an absolute URL within an HTML document.

So what does this mean?

  1. In the context of using a <base> element to resolve a URL, the browser treats links beginning with / as absolute URLs. Yes, I think most of us would consider links beginning with / to be relative URLs, so the terminology is inconsistent.
  2. You have to be careful when constructing your markdown links. With the <base> element set as described above:

baseURL = 'https://example.org/'

Markdown Resolves To
[root](/) https://example.org/
[site](.) https://example.org/
[about](about/) https://example.org/about/
[book-2](books/book-2/) https://example.org/books/book-2/</a>
[video-2](videos/video-2/) https://example.org/videos/video-2/
[fragment](books/book-1#heading-4) https://example.org/books/book-1#heading-4

baseURL = 'https://example.org/hugo/'

Markdown Resolves To
[root](/) https://example.org/
[site](.) https://example.org/hugo/
[about](about/) https://example.org/hugo/about/
[book-2](books/book-2/) https://example.org/hugo/books/book-2/</a>
[video-2](videos/video-2/) https://example.org/hugo/videos/video-2/
[fragment](books/book-1#heading-4) https://example.org/hugo/books/book-1#heading-4

Pros:

  • One line of template code
  • Works when serving from server root or subdirectory

Cons:

  • Same-page fragment URLs must specify entire path
  • Auto-generated table of contents will be broken due to same-page fragment URLs
  • canonifyURLs = true will have no effect because none of the URLs are root-relative (except for root
  • relativeURLs = true will have no effect because none of the URLs are root-relative (except for root)
  • uglyURLs = true will break the site

While adding a <base> element and carefully constructing your links may solve your markdown problems, other theme elements (images, icons, CSS, JS, fonts, menus, etc.) may still be broken when trying to serve your site from a subdirectory. It depends on how the theme was built.

Method 2: RelRef Shortcode and Leaf Bundles

Hugo has a built-in relref shortcode to help you construct links in your markdown:

[about]({{< relref "/about" >}} 
baseURL Resolves To
http://example.org/ http://example.org/about/
http://example.org/hugo/ http://example.org/hugo/about/

Pros:

  • No template changes required
  • Works when serving from server root or subdirectory
  • Same-page fragment URLs do not require entire path (i.e., use #heading)
  • Auto-generated table of contents will be correct
  • canonifyURLs = true works as designed
  • relativeURLs = true works as designed
  • uglyURLs = true works as designed

Cons:

  • The relref shortcode works for links in your markdown, not for images.
    This does not work:

    ![My cat]({{< relref "/images/cat.jpg" >}})
    

For a site that you can serve from the server root or from a subdirectory, use Leaf Bundles:

content/blog/
└── post-1/
    ├── cat.jpg
    └── index.md

And use this markdown:

![My cat](cat.jpg)

While using the relref shortcode and leaf bundles may solve your markdown problems, other theme elements (images, icons, CSS, JS, fonts, menus, etc.) may still be broken when trying to serve your site from a subdirectory. It depends on how the theme was built.

Method 3: Render Hooks (Advanced)

You can create markdown render hooks for links, images, and headings. This allows you to intercept the markdown for these elements and render them as you wish.

@bep created an example of the portability benefits here:
https://github.com/bep/portable-hugo-links

But, just as with the other two methods…

While using markdown render hooks may solve your markdown problems, other theme elements (images, icons, CSS, JS, fonts, menus, etc.) may still be broken when trying to serve your site from a subdirectory. It depends on how the theme was built.

3 Likes

Ok , but I use no theme , and what I have :

baseURL =

Analyse of html source in browser , I get

with canonifyurls set to true , link render as …href=>link…

with canonifyurls set to false , the same render as …href=>link…

So where the part /hugo gone in baseURL ? It seems of baseURL is truncated when canonifysURLs is set to false

Also , the solution with ref or relref is only usable with markdown files , not with images,pdf,tetx or whatever

So when rendering to HTML , if I understand , with canonifyURLS set , hugo prepend baseURL to the link in mardown , but if not set it prepend only the domain of baseURL but not the subdirectory (where is it gone ?)

Thanks for you answer.

.

Yes, but only if the link destination begins with /.

No, it does not prepend anything. To verify, run hugo and look at the HTML file. When you hover your cursor over a relative link in the browser window, the browser prepends the current base URL.

Also, please read:
https://discourse.gohugo.io/t/sharing-code-in-the-forums/8968

Thanks.

What is the difference between .Site.BaseURL and the current base URL .

With Site.BaseURL = the current base URL stays

And with Site.BaseURL= the current base URLS stays , but it must be

That is the problem : you have a base url , link to /posts MUST becomes , no .

Again thanks . but I have to prepend /hugo to all urls beginning with / because baseURL is not realy base URL. Why define a baseURL if it is not realy used.

Previous reply was not really clear (words missing).
What I later found when using subdirectory in .Site.BaseURL = /http://example.com/hugo/

  • ref is always working (full URL)
  • relref also always working in browser , but gives 2 different values always beginning with “/”
    1. /hugo/file when canonifyurls is false : ok browser prepend base of URL
    2. /file when canonifyurls is true : ok hugo prepend .Site.BaseURL
  • markdown [test](test) always ok , browser prepend current URL
  • markdown [test](/test) (link begin with /)
    1.canonifyurls true : ok hugo prepend .Site.BaseURL
    2.canonifyurls false : NOK , browser prepend base URL (browser does not understand subdirectory and hugo part in URL is gone)

Others problems I found when canonifyurls = true , are solved with adaptation off templates in themes .
(Templates developped without subdirectory = does not see these problems)

So canonifyurls is needed is case of subdirectory, but some templates must be adapted.

Thanks for your time

Man, what an amazing post. Too bad it’s “pearls before…” and all that.