The Adventures of the Retired Guy

Technology, sailing and the rest of life.

Lessons learned in Hugo config

Configuration gotchas

[Solved] UglifyURLs and CanonifyURLs

I’m trying to keep images in same folder as post they belong to. That’s an easy thing to manage in Typora (and other markdown editors).

But I’m having trouble using simple relative image links, e.g ![foo](foo.jpg) to work. I can’t seem to get hugo site generation to put the files in the right place in the generated site (in fact, they’re not being moved anywhere.)

On the one hand, I want to host images in same folder as .md file. (I also want flexibility to use page bundles to put a collection of files in a folder with an

On the other hand, I want search, archive and about links defined by stack theme to just work.

But default config is:

UglifyURLs = false
CanonifyURLs = false
    post = "/post/:filename"
    page = "/:filename"
    project = "/project/:filename"

Where the pages links (about, search, archive) do work, but local images with relative links do not.

Pages links (from home page) are: http:/localhost/about/

But relative links in posts, e.g ![foo](foo.png) get 404. Hugo site build with this config created www/post/<filename>/index.html (as expected), but did not copy image files into this folder. In fact, image files are left right where I put them, in www/content/post/foo.png.

OTOH, this config:

UglifyURLs = true
CanonifyURLs = true
    post = "/post/:filename"
    page = "/:filename"
    project = "/project/:filename"

Breaks pages links, but rescues local relative images.

For Pages, the homepage link is still http://localhost/about/, but what actually does work is: http://localhost/about.html (and likewise for archive and search)

For posts, the post url is now: http://localhost/post/<filename>.html, and the image url is: sample (with Hugo also creating resized versions with other filenames).

The fix

2 unrelated issues at play here:

  1. UglifyURLs = true is needed, then relative URLs just work. But I was not seeing proper operation on each rebuild because of cruft left in destination directory from prior builds. Need to periodically delete the target and rebuild.
  2. Problem with search, home and archives links was configuration. config.toml has hardcoded links for these menu entries, and they need to be manually adjusted when you switch between pretty and ugly URLs. The actual path for these pages is determined by [permalink][page], so I opted for the cleaner looking page = /:slug. Can’t calculate anything in config.toml. Maybe there would be a way to automate this in the theme?

But wait – there’s more! Later on, I found the archive widget still had a problem, there was another place it was assuming “really pretty” URLs (no trailing slash, unlike Hugo “pretty”-ish URLs). And search widget had a hardcoded action="/search". So the (so far) final fix I settled on was to undo the previous config fixes, leave all 3 with “really” pretty urls then add this in the frontmatter of each of the pages.

url = "/search"

(e.g for /content/page/ This forces this particular content item to have just that URL, regardless of config: .site.uglyURLS.

This is a continuation of the issues above. The post itself can use relative links to embedded images and to the cover image (front matter: image = "cover.jpg").

However, the cover image link doesn’t work when the post is listed in home page, search or taxonomy lists. Reason is the path to the list is /tags/blog/index.html and the code for the list doesn’t fix the url from the front matter (the actual image is somewhere under /blog...). This does work from a page bundle, but only because theme partial/helper/image has some dense code to handle resources (which sounds like an overall good thing anyway).

Hugo doesn’t seem to have a function that would compute the right path for me (!? what I need is `absolute or relative url with context of other page' ). There may turn out to be one as I dig further, but for now, I’m just going to convert all the pages to page bundles and provide an archetype to create new blog posts as bundles too.

This is overall probably a good thing anyway, it will keep the blog directory cleaner and many Hugo will work some magic on image processing for these images for me.

Now that I’ve standardized on ugly URLs but every page being a page bundle, my /content/blog folder is just a long list of subfolders. This is beginning to get unweildy since I don’t (want to) use a naming convention to create a natural list order (like date). The old, stable posts make it hard to focus on the recent evolving posts. So I wanted to create a family of subfolders to move the stable posts out of the way, e.g:


But I found initially that moving a post (folder) into a subfolder changed its permalink too. E.g /blog/a-meditation-on-projects became /blog/older-0/a-meditation-on-projects. Not horrible, but defeats the notion of permalinks. This turned out to be a config issue.

# Config.toml

    blog = "/blog/:filename"

Changed this to:

# Config.toml

    blog = "/blog/:slug"

And all just works. I can freely move posts among the folders and subfolders and the page link does not change.

I had initially settled on :filename to avoid having to specify Title or Slug in front matter of each post. Seems the file (folder) name should be sufficient… Now I must add one configuration item in each post and have settled on it being the Title, since that incldues the capitilization information which cannot be recovered from slug or filename. I’ve added slug and title to the archetype for posts (both based on .Name), so the title can be freely changed later and doesn’t affect the slug (and permalink).

The one remaining wrinkle in this is that Hugo doesn’t protect against duplicate slugs. Post file names will be unique in the same content folder, and the slug will (initially) match that. But if I move a post into a subfolder, nothing prevents me from creating a new post with the same slug. And Hugo by default, will happily hide one or the other post with that slug. Hugo does have a --path-warnings flag, but it doesn’t work with hugo server and won’t stop a remote rebuild (not an error). This seems actively hostile, but I can probably manage for now and complain in the meantime.