Understanding relative URLs

Setup:

config.toml
content/_index.html
content/home/index.md

In config.toml I have relativeURLs = "true".
In content/home/index.md I have:

{{< relurl "index.html" >}}
{{< relurl "/index.html" >}}
{{< relurl "../index.html" >}}
{{< relurl "./index.html" >}}

relurl is a trivial shortcode wrapper for relURL:

{{ .Get 0 | relURL }}

The generated public/home/index.html contains four times /index.html.
I would have expected:

{{< relurl "index.html" >}}     index.html or ./index.html
{{< relurl "/index.html" >}}    /index.html
{{< relurl "../index.html" >}}  ../index.html
{{< relurl "./index.html" >}}   ./index.html or index.html

Apparently I’m misunderstanding something basic. But what?

relURL returns a path relative to the protocol+host portion of the baseURL specified in config.toml.

Example 1

baseURL = "https://www.example.org/"

The baseURL protocol+host is https://www.example.org
The baseURL path is /

{{< relurl "quez.html" >}}    --> /quez.html
{{< relurl "/quez.html" >}}   --> /quez.html
{{< relurl "../quez.html" >}} --> /quez.html
{{< relurl "./quez.html" >}}  --> /quez.html

Example 2

baseURL = "https://www.example.org/foo/bar/baz/"

The baseURL protocol+host is https://www.example.org
The baseURL path is /foo/bar/baz/

{{< relurl "quez.html" >}}    --> /foo/bar/baz/quez.html
{{< relurl "/quez.html" >}}   --> /foo/bar/baz/quez.html
{{< relurl "../quez.html" >}} --> /foo/bar/quez.html
{{< relurl "./quez.html" >}}  --> /foo/bar/baz/quez.html

How It Works

The relURL function uses path.Join to assemble the return value.

Join joins any number of path elements into a single path, separating them with slashes. Empty elements are ignored. The result is Cleaned.

What does “Cleaned” mean? From the path.Clean documentation:

Clean returns the shortest path name equivalent to path by purely lexical processing. It applies the following rules iteratively until no further processing can be done:

1. Replace multiple slashes with a single slash.
2. Eliminate each . path name element (the current directory).
3. Eliminate each inner .. path name element (the parent directory)
   along with the non-.. element that precedes it.
4. Eliminate .. elements that begin a rooted path:
   that is, replace "/.." by "/" at the beginning of a path.

This was helpful:

Setting relativeURLs = true in config.toml has no effect when running hugo server. It only affects the output generated when you build the site with hugo.

2 Likes

That is not true, but I admit it that the relativeURLs = true feature is … confusing. But you are right in your above explanation. I think the important part is that.

  • relURL and .RelPermalink is conceptually the same
  • relativeURLs = true is something else, and works on the final output and should (if you’re diciplined enough) create URLs that works without a web server root (e.g. by clicking on the file on disk).
3 Likes

This is the crucial piece of information (emphasis mine):

relURL returns a path relative to the protocol+host portion of the baseURL specified in config.toml.

The docs on relURL read (emphasis, again, mine):

Given a string, prepends the relative URL according to a page’s position in the project directory structure.

This makes me expect {{< relurl "../index.html" >}} to return /foo/index.html when invoked from e.g. /foo/bar/doc.md.

OK, the docs was wrong – I made a quick adjustment. relURL does not know/care about “the page position”.

1 Like

Thanks!

Related:

relref . "feedback/index.html"

yields /feedback/. While this is correct for web sites (that by default serve an index.html when asked for a folder) it defeats the possibiliity to use the static files directly from disk (as suggested in https://gohugo.io/content-management/urls/#relative-urls).

Or is this the “It can’t be used for the homepage, section pages, etc.” exception?

The “serves static files from disk” only works with relativeURLs=true.

Yes, that’s what I have in the config.

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.