BaseURL path (subdir) sometimes ignored by relURL, absURL

=>

Both absURL and relURL consider the configured value of baseURL in your site’s config file.

Here’s a test scenario (using content/index.md and layouts/_default/single.html)

  <pre>
  .Site.BaseURL    : {{ .Site.BaseURL     }}
  <br/>
  {{- $myRefRel := `vendor/cool.css`      }}
  $myRefRel        : {{ $myRefRel         }}
  relURL $myRefRel : {{ relURL $myRefRel  }}
  absURL $myRefRel : {{ absURL $myRefRel  }}
  <br/>
  {{- $myRefAbs := `/vendor/cool.css`     }}
  $myRefAbs        : {{ $myRefAbs         }}
  relURL $myRefAbs : {{ relURL $myRefAbs  }}
  absURL $myRefAbs : {{ absURL $myRefAbs  }}
hugo -d ${DEST}/ -b "http://example.com/theme/w3css/"

gives me:

  .Site.BaseURL    : http://example.com/theme/w3css/

  $myRefRel        : vendor/cool.css
  relURL $myRefRel : /theme/w3css/vendor/cool.css
  absURL $myRefRel : http://example.com/theme/w3css/vendor/cool.css

  $myRefAbs        : /vendor/cool.css
  relURL $myRefAbs : /theme/w3css/vendor/cool.css
  absURL $myRefAbs : http://example.com/vendor/cool.css

absURL "/vendor/cool.css" produces http://example.com/vendor/cool.css
I would expect:
http://example.com/theme/w3css/vendor/cool.css
since the BaseURL defines /theme/w3css/ as “subdir”.

Things change if the deprecated --canonifyURLs is used (which is used to build the exampleSites for https://themes.gohugo.io/)

hugo -d ${DEST}/canonify --canonifyURLs -b "http://example.com/theme/w3css/"

we get:

  .Site.BaseURL    : http://example.com/theme/w3css/

  $myRefRel        : vendor/cool.css
  relURL $myRefRel : /vendor/cool.css
  absURL $myRefRel : http://example.com/theme/w3css/vendor/cool.css

  $myRefAbs        : /vendor/cool.css
  relURL $myRefAbs : /vendor/cool.css
  absURL $myRefAbs : http://example.com/vendor/cool.css

If we take the configured BaseURL for granted (so the site will live in a subdir (/theme/w3css) on the host) the only valid test is
absURL $myRefRel
all others do not include the defined subdir from the BaseURL.

Maybe I’ve a total misunderstanding here?

3 Likes

I have been annoyed by this issue too. At work, for my project site on Gitlab CE Pages, the baseURL needs to be like that i.e. https://example.com/project/.

The only workaround is to set the canonifyURLs to the non-default value of true in the site config.

I wish the absURL and relURL did the right thing even with canonifyURLs set the the default false.

Thanks for bringing this up. I believe this needs an issue to be opened.


Also, I believe that the behavior of relURL $myRefAbs and absURL $myRefAbs in your second case (canonifyURLs true) is correct because you are specifying / in the beginning of the relative link. With this behavior, you can link to “sister sub-directories” under the same domain.

Actually… my brain’s jumbled up at the moment… I need to get back on this topic after revisiting my notes.

It was something like “relURL was unusable for baseURLs of this kind”… but I’ll confirm. No, the problem was with using figure shortcodes in sites with such baseURLs where canonifyURLs = false as explained here.

I’ve put together 2 test sites with baseURLs with subdirs; one with canonyURLs set to false (default), and another true.

Test sites:

Note that even if they look like just 2 regular pages, they are technically separate Hugo sites.

Pinging @bep to hopefully chime into this… but the issue I faced for an internal webpage at work was that the links did not work the same way when switching from canonifyURLs true to false… many links broke… as we see in the above 2 test cases too.

My understanding is that canonifyURLs should only change the link aesthetics… absolute URL vs relative URL… should it also change the whole link generation behavior too?


Little post on my Hugo Sandbox site linking to those 2 sites.


Just revisited my notes:

So I had to stick with canonifyURLs = true for that internal site I referenced earlier, because it broke all the figure shortcodes that looked like below (when canonifyURLs = false):

{{< figure src="/images/foo.png" >}}

as we see here:

@it-gro

After spending quite some time on this and comparing these:

The behavior of absURL and relURL looks consistent to me whether canonifyURLs is true or false… consistent in the sense that they finally resolve to the same links in HTML (even though they might look different at template level). – Well, not exactly… see my next post below.

For example, all the gopher images on either of the above 2 linked pages are from the exact same location https://hugo-sandbox.netlify.com/subdir-canonify-true/images/gopher.png (for the the subdir site with canonifyURLs = true) and similarly https://hugo-sandbox.netlify.com/subdir-canonify-false/images/gopher.png for the other subdir site.

The outputs of absURL and relURL look different, but eventually based on the value of canonifyURLs, they resolve to the same thing.

I hope this makes sense.


As for my site-breaking issue, I believe it needs to be taken care of in the figure shortcode [my proposal for that]. Sorry that that issue had nothing to do with your main question.

Still pondering on this…

While the canonifyURLs true/false results are consistent… I feel that the relURL vs absURL results are not consistent when the link begins with / (and that’s true for either value of canonifyURLs):

Looking at this:

  • relURL $myRefAbs results in /subdir-canonify-false/images/gopher.png
  • absURL $myRefAbs results in https://hugo-sandbox.netlify.com/images/gopher.png

Shouldn’t both either include or both not include the /subdir-canonify-false portion? We see a consistency in this when the link is relative (when it does not begin with /), but not in this case when the link is absolute (begins with /)… Why so?

1 Like

=>

–baseURL string hostname (and path) to the root, e.g. http://spf13.com/

It is my understanding that with baseURL we define the root.
Since it is not: –baseURL defines the protocol (http/https) and the host (domain).

It is unexpected for me, that relURL, absURL (with or without canonify) create sometimes URLs outside of this root. And it is not how I understand the relURL and absURL documentation (see above).

To give an example how I’m getting hit by this:

<script src='{{ `/vendor/jquery/jquery-3.2.1.slim.min.js` | absURL }}'></script> 

rendering with:

HUGO_CANONIFYURLS=false hugo --baseURL "http://example.com/foo/bar" --themesDir ../../  --cleanDestinationDir --destination /data/scratch/tmp/no_canonify
HUGO_CANONIFYURLS=true  hugo --baseURL "http://example.com/foo/bar" --themesDir ../../  --cleanDestinationDir --destination /data/scratch/tmp/canonify

both ways I’m ending up with

<script src='http://example.com/vendor/jquery/jquery-3.2.1.slim.min.js'></script>

but the root is defined as http://example.com/foo/bar this is where
the vendor directory is. So absURL is not an option (but does it works
as expected?).

So using relURL with this:

<script src='{{ `/vendor/jquery/jquery-3.2.1.slim.min.js` | relURL }}'></script>
<script>
  function setStyleSheet(id){
    var cssName    = document.getElementById(id).value;
    var stylesheet = document.getElementById("theme-colors");
    var cssPrefix  = '{{ relURL `/vendor/w3css/4/`}}';
    var href       = cssPrefix.concat(cssName);
    stylesheet.setAttribute('href', href);
  }
</script>

again doing:

HUGO_CANONIFYURLS=false hugo --baseURL "https://example.com/foo/bar" --themesDir ../../  --cleanDestinationDir --destination /data/scratch/tmp/no_canonify
HUGO_CANONIFYURLS=true  hugo --baseURL "https://example.com/foo/bar" --themesDir ../../  --cleanDestinationDir --destination /data/scratch/tmp/canonify

I end up with:

HUGO_CANONIFYURLS=false:

<script src='/foo/bar/vendor/jquery/jquery-3.2.1.slim.min.js'></script> 
<script>
  function setStyleSheet(id){
    var cssName    = document.getElementById(id).value;
    var stylesheet = document.getElementById("theme-colors");
    var cssPrefix  = '\/foo\/bar\/vendor\/w3css\/4\/';
    var href       = cssPrefix.concat(cssName);
    stylesheet.setAttribute('href', href);
  }
</script>

HUGO_CANONIFYURLS=true:

<script src='http://example.com/foo/bar/vendor/jquery/jquery-3.2.1.slim.min.js'></script> 
  <script>
    function setStyleSheet(id){
      var cssName    = document.getElementById(id).value;
      var stylesheet = document.getElementById("theme-colors");
      var cssPrefix  = '\/vendor\/w3css\/4\/';
      var href       = cssPrefix.concat(cssName);
      stylesheet.setAttribute('href', href);
    }
  </script>

<script src= is fine for me.
But note the content of the cssPrefix var.
With HUGO_CANONIFYURLS=true the BaseURL path get’s lost.

I see that hugo is aware that src=‘any_url’ any_url is building up a link in HTML which is not the same as for the cssPrefix var.

EDIT: absURL is an option see below

I don’t have time to look into this in detail, but one tip is this:

The canonifyURLs is working on the rendered content, so it can not have any advanced logic. That is why, when canonifyURLs=true, the path is not included when doing relURL – but is re-added at render time (eg "baseURL (including subpath) + any relative URL (including those built with relURL)).

Thanks @bep!

Update - showing ALL combinations:

<script src='{{ `/vendor/jquery/jquery-3.2.1.slim.min.js` | absURL }}'></script> 
<script src='{{ `vendor/jquery/jquery-3.2.1.slim.min.js` | absURL }}'></script> 
<script src='{{ `/vendor/jquery/jquery-3.2.1.slim.min.js` | relURL }}'></script> 
<script src='{{ `vendor/jquery/jquery-3.2.1.slim.min.js` | relURL }}'></script> 
  <script>
    function setStyleSheet(id){
      var cssName    = document.getElementById(id).value;
      var stylesheet = document.getElementById("theme-colors");
      var cssPrefix  = '{{ absURL `/vendor/w3css/4/`}}';
      var cssPrefix  = '{{ absURL `vendor/w3css/4/`}}';
      var cssPrefix  = '{{ relURL `/vendor/w3css/4/`}}';
      var cssPrefix  = '{{ relURL `vendor/w3css/4/`}}';
      var href       = cssPrefix.concat(cssName);
      stylesheet.setAttribute('href', href);
    }
  </script>

HUGO_CANONIFYURLS=false:

<script src='http://example.com/vendor/jquery/jquery-3.2.1.slim.min.js'></script> 
<script src='http://example.com/foo/bar/vendor/jquery/jquery-3.2.1.slim.min.js'></script> 
<script src='/foo/bar/vendor/jquery/jquery-3.2.1.slim.min.js'></script> 
<script src='/foo/bar/vendor/jquery/jquery-3.2.1.slim.min.js'></script> 
  <script>
    function setStyleSheet(id){
      var cssName    = document.getElementById(id).value;
      var stylesheet = document.getElementById("theme-colors");
      var cssPrefix  = 'http:\/\/example.com\/vendor\/w3css\/4\/';
      var cssPrefix  = 'http:\/\/example.com\/foo\/bar\/vendor\/w3css\/4\/';
      var cssPrefix  = '\/foo\/bar\/vendor\/w3css\/4\/';
      var cssPrefix  = '\/foo\/bar\/vendor\/w3css\/4\/';
      var href       = cssPrefix.concat(cssName);
      stylesheet.setAttribute('href', href);
    }
  </script>

HUGO_CANONIFYURLS=true:

<script src='http://example.com/vendor/jquery/jquery-3.2.1.slim.min.js'></script> 
<script src='http://example.com/foo/bar/vendor/jquery/jquery-3.2.1.slim.min.js'></script> 
<script src='http://example.com/foo/bar/vendor/jquery/jquery-3.2.1.slim.min.js'></script> 
<script src='http://example.com/foo/bar/vendor/jquery/jquery-3.2.1.slim.min.js'></script> 
  <script>
    function setStyleSheet(id){
      var cssName    = document.getElementById(id).value;
      var stylesheet = document.getElementById("theme-colors");
      var cssPrefix  = 'http:\/\/example.com\/vendor\/w3css\/4\/';
      var cssPrefix  = 'http:\/\/example.com\/foo\/bar\/vendor\/w3css\/4\/';
      var cssPrefix  = '\/vendor\/w3css\/4\/';
      var cssPrefix  = '\/vendor\/w3css\/4\/';
      var href       = cssPrefix.concat(cssName);
      stylesheet.setAttribute('href', href);
    }
  </script>

So absURL with a path not starting with / does work as I would expect (BaseURL is defining the root - not just the domain).
With relURL and canonifyurls the subdir gets lost in the url-string (cssPrefix).

So I start using absURL with a path to the resources not starting with / now …
absUrl “vendor/cool.css” and not absURL “/vendor/cool.css” since this works (in my use cases)

Yes, looks like that’s the only way possible as you might need to support both values of canonifyURLs for your theme.

Also, I cannot find it, but there was an issue with link canonification not working as expected within CSS.

While looking for it, I came across this, so now this whole thread feels like deja vu:

--canonifyURLs is deprecated, the cli switch will vanish (as of 0.40?) but HUGO_CANONIFYURLS=false hugo is still going to work and probably the exampleSites under https://themes.gohugo.io/ will still be build using HUGO_CANONIFYURLS (?). So dropping the support for canonifyURLs is not an option for the listed themes…

Right, I wasn’t implying that the support will or should be dropped. As I said earlier, I myself need to set that for my work internal site.

I was saying that you would need to support both as all your theme users wouldn’t possibly be setting canonyURLs the same way.


Also, the command-line switch for this option was deprecated because, as I understand, this option is not something someone would just add or remove on the fly. People usually pick one option and stick with it, so it’s better suited to be set just in the site config file (which auto-extends to the corresponding environment variable).

Hmm
If a theme dev wants to have the exampleSite shown on themes.gohugo.io there is no “pick one option”. It will be build using canonifyURLs (at least currently).

Another (shorted) example (with relURL)

{{- $myFaviconVersion := default `a_random_value`   $.Site.Params.favicon.version }}
{{- $myFaviconPath    := default `/images/favicons` $.Site.Params.favicon.path    }}
  <link rel="shortcut icon"                         href='{{(printf `%s/%s?v=%s` $myFaviconPath `favicon.ico`                $myFaviconVersion ) | relURL }}' />
  <meta name="msapplication-TileImage"           content='{{(printf `%s/%s?v=%s` $myFaviconPath `mstile-144x144.png`         $myFaviconVersion ) | relURL }}' />

works with BaseURL (with subdir) and HUGO_CANONIFYURLS=false (the default)

 <link rel="shortcut icon"                         href='/foo/bar/images/favicons/favicon.ico?v=a_random_value' />
  <meta name="msapplication-TileImage"           content='/foo/bar/images/favicons/mstile-144x144.png?v=a_random_value' />

but works not with HUGO_CANONIFYURLS=true

  <link rel="shortcut icon"                         href='http://example.com/foo/bar/images/favicons/favicon.ico?v=a_random_value' />
  <meta name="msapplication-TileImage"           content='/images/favicons/mstile-144x144.png?v=a_random_value' />

possible fix:

  <meta name="msapplication-TileImage"           content='{{(printf `./%s/%s?v=%s` $myFaviconPath `mstile-144x144.png`         $myFaviconVersion ) | absURL }}' />

(note the ./ in the printf)

So it is probably expected that I know which attributes hugo recognises as a “link” and are processed differently (e.g. href vs content)