How is the Canonical page determined?

Hugo 0.154.4 introduced the Canonical output format. How is the canonical chosen? I’ve scanned the docs and not found an answer.

Hugo seems to choose the last format that renders HTML when there is more than one. Is that how it chooses?

For example, Docsy’s website has the following specified outputs:

outputs:
  ...
  section: [HTML, RSS, print]

With this setup I’ve noticed that print is used as canonical. ~If I swap HTML and print, I get the expected (non-print) canonical.~ (Edit: swapping breaks other things, see below).

I’m going to fix the Docsy config, but would welcome a reference to the docs stating how the canonical URL is chosen. Thanks!

Oops, nope, that didn’t work. Per Outputs per page kind:

The order of the output formats in the arrays above is important. The first element will be the primary output format for that page kind, and in most cases that should be html as shown in the default configuration.

Let me get a link to an example: … see below.

It chooses the first output format, if any, that has rel=canonical defined in the output formats definition. I don’t know how your print output format looks like …?

Here’s Docsy’s print definition:

outputFormats:
  PRINT:
    baseName: index
    isHTML: true
    mediaType: text/html
    path: _print
    permalinkable: false

Here’s an example of the behavior I’m seeing:

If we inspect the page (and pretty-print it):

$ curl https://deploy-preview-2434--docsydocs.netlify.app/docs/getting-started/
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>http://localhost/_print/docs/get-started/</title>
    <link rel="canonical" href="http://localhost/docs/get-started/">
    <meta charset="utf-8">
    <meta http-equiv="refresh" content="0; url=http://localhost/_print/docs/get-started/">
  </head>
</html>

Oh! I just noticed that the rel=canonical is actually correct! It’s the meta refresh URL that is wrong.

Here’s a small repro: hugo-alias-and-print-test.zip. It’s features are:

  • Two languages, en and fr
  • fr pages have en pages as fallback
  • en/about/_index.md declaresaliases: [info] in the front matter
  • The config defines a print output for sections.

Building the site and inspecting the alias file:

$ hugo
Start building sites … 
hugo v0.154.5-a6f99cca223a29cad1d4cdaa6a1a90508ac1da71+extended darwin/arm64 BuildDate=2026-01-11T20:53:23Z VendorInfo=gohugoio


                  │ EN │ FR 
──────────────────┼────┼────
 Pages            │ 10 │  9 
 Paginator pages  │  0 │  0 
 Non-page files   │  0 │  0 
 Static files     │  0 │  0 
 Processed images │  0 │  0 
 Aliases          │  3 │  2 
 Cleaned          │  0 │  0 

Total in 21 ms
$  cat public/info/index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>https://example.org/_print/about/</title>
    <link rel="canonical" href="https://example.org/about/">
    <meta charset="utf-8">
    <meta http-equiv="refresh" content="0; url=https://example.org/_print/about/">
  </head>
</html>

Notice how the alias file has the right canonical, but is refreshes to the print version of the page rather than the canonical version of the page.

I guess that this happens because aliases get processed for the print pages too? Is this expected? Can it be disabled, or does something need to be reconfigured? Thanks!

I’m not sure what happens in your case, but I suggest you set a value for rel in your config of print, which should fix it, see

Setting rel: alternate doesn’t change the behavior shown in the repro site.

Reference: https://github.com/gohugoio/hugo/issues/14381