Template menu generation .URL messed up using relativeURLs: true

I wanted to use relative URLs because that would make writing markdown links easier. Given me having

BaseURL: "http://www.example.com/~userdir"

in my config.yaml.

My goal was to be able to link to it using the following markdown

blabla [page1](/section1/page1) blablabla

to link to the file content/section1/page1.md where the / would refer to the BaseURL. This worked great, but unfortunately my menu generated by my template got messed up. {{ .URL }} ended up being rendered as http://www.example.com/~userdir/~userdir/section/page.

Here is my menu template for reference (basically the snippet shown in the documentation):

{{ $currentNode := . }}
{{ range .Site.Menus.main }}
  <!-- output child elements -->
  {{ if .HasChildren }}
            <!-- first the lvl 1 node -->
            <li {{ if $currentNode.HasMenuCurrent "main" . }} class="active section"{{ else }} class="inactive section"{{ end }}><a href="{{ .URL }}">{{ .Name }}</a>
              <!-- then the children inside a <ul> using range .Children where . refers to a child node -->
              <ul {{ if $currentNode.HasMenuCurrent "main" . }} class="expanded"{{ else }} class="collapsed"{{ end }}>
    {{ range .Children }}
                  <li {{if $currentNode.IsMenuCurrent "main" . }} class="active"{{ end }}><a href="{{ .URL }}">{{ .Name }}</a></li>
    {{ end }}
  <!-- no child elements, so just output the top node -->
  {{ else }}
            <li {{if $currentNode.IsMenuCurrent "main" . }} class="active"{{ end }}><a href="{{ .URL }}">{{ .Name }}</a></li>
  {{ end }}
{{ end }}

Anybody have any idea on what might be causing this?

It seems that .URL is built prepended with the base url by default. You can use the relURL template function to get the relative url that you expected.

Thanks for the help, however, what I had to use was absURL!

I was a bit wrong in my original post as I hovered the links rather than checking the source. Google Chrome showed the link as http://www.example.com/~userdir/~userdir/section/page2 but they were actually rendered as ../~userdir/section1/page2. So rather than just having {{ .URL }} I had to use {{ .URL | absURL }}.

This might however be a bug, since the real relative URL given that my current URL was http://www.example.com/~userdir/section1/page1 should have been ./page2 or ../section1/page2.

Is this a bug?

That doesn’t make much sense.

Yes, I realized that it might be a trailing slash issue that I had not paid any attention to (what the browser interpretes the current location as).

What I mean is that if the current page is

URL1: http://www.example.com/~userdir/section1/page1/

and I have a second page at

URL2: http://www.example.com/~userdir/section1/page2/

the relative URL to URL2 from URL1 should be one of the following (as they are equivalent)


What Hugo gave me was

URL3: ../~userdir/section1/page2

which would have been correct if the current URL had been http://www.example.com/~userdir/section1/page1 i.e. URL1 without the trailing slash.

Could it be that the internal representation of the URL is without a trailing slash but when the browser visits the URL a trailing slash is added since it is a directory when using pretty URLs?

I tried switching to uglyurls: true which does not have any issues with the generated relative URLs.

Edit: … unless I am on an index.html page whose URL is shown without the actual index.html just the parent directory with a trailing slash.

I think you are misunderstanding what Hugo means by a relative URL (I initially tripped over this, too). A relative URL is simply a URL path without a hostname (or user, scheme, etc). It doesn’t mean that the path itself is a relative path. So, ../page2/ and /~userdir/section1/page2/ are both relative URLs to Hugo.

What you are talking about is canonicalization of relative URLs. Read the docs and look over the relURL test cases to see how canonifyURLs works.

1 Like

Ah, ok. Then I get it. Thanks for the info and the link to the test cases!

I kind of think the responses went off on a tangent to the original issue after the first (and helpful) response by digitalcraftsman.

The menu implementation in Hugo does prepend the URLs or do some version of relURL type manipulation of them. The issue is that the relativeURLs setting also does it’s thing. While on a site hosted directly at a root dir level (base URL “/”) that’s no big deal, it breaks under project rooted sites.

Given the config setting below, the actual url <a href="{{ .URL }}"> writes in the html file is <a href="./examplesite/tags">. (as was understood above)

The suggestion to use relURL to get the relative url you expect doesn’t make sense to me. You almost need the opposite :slight_smile: - a way to get back the original url so that relativeURL can process the original. If you cal relURL on .URL you’ll just end up getting another level of the path components of baseurl.
name = "tags"
url = "/tags"
identifier = "fa fa-tags"
weight = 5

{{ printf "(%#v), relurl is (%s)" .URL (relURL .URL) }} prints:
(&#34;/examplesite/tags&#34;), relurl is (/examplesite/examplesite/tags)

I guess one could get the base url from the site variables and do a string edit to remove it from the .URL, but seems hacky. The nav bar implementation is often in theme code, and it’s always nice if theme code would actually work without requiring or assuming final site destination (subdir vs. root) or config settings like relativeURL, canonifyURLs, etc.

I know this is an old topic, but I think it’s still a relevant and open one. Is there a best practices topic for writing good url handling in themes, site buildings, etc. taking into account relative urls? Any suggestion on what the cleanest thing to do in this situation would be? As the original poster discovered, you could punt on the issue and use absURL but that blows the benefits of staying relative away of course.

Note linked topic. If relativeURLs=true then setting canonifyURLs=true will make the URL manipulation done by the menu code not include context root, and therefore not get the path doubled as I posted above. In this case, the canonifyURLs=true will not make all the relative urls canonicalized into absolute urls as one might expect given the docs.