My Fix for Clean Hugo URLs with Dated Folders It’s the simple configuration details that make or break a project. Here’s how a one-line change cleaned up my entire site structure.


👋 Hey everyone,

It’s been a week of deep focus on my blog, orioninsist.org. As I continue to migrate more of my writing and build out my content strategy, I’ve become obsessed with long-term sustainability. For me, that means organization. I’m not just talking about tags and categories; I mean the actual file structure on my machine. A clean, logical directory is like a well-organized workshop—it makes finding things, making changes, and scaling up so much easier.

As my post count grew, I decided that the best way to manage my content was to organize it by date, right in the filesystem. It seemed so logical, so clean. But this simple organizational goal quickly led me down a rabbit hole of configuration hell, resulting in URLs that were anything but clean. This is the story of how I hit a wall and then broke through it with a solution that was hiding in plain sight.


My Goal This Week 🎯

My objective was straightforward: I wanted to organize my Markdown files for the blog inside a nested folder structure based on the year and month of publication.

My ideal content directory looked like this:

content/
└── blog/
    ├── 2024/
    │   └── 10/
    │       └── an-older-post.md
    └── 2025/
        ├── 09/
        │   └── another-great-post.md
        └── 10/
            └── my-newest-post.md

This structure is fantastic for maintenance. If I need to find an article from October 2024, I know exactly where to look. It prevents a single blog folder from becoming a chaotic mess of hundreds of files.

However, I had a non-negotiable requirement for the user-facing side of things: the URLs had to remain clean, simple, and timeless. I wanted the URL for my-newest-post.md to be:

https://orioninsist.org/blog/my-newest-post/

And NOT this monstrosity:

https://orioninsist.org/blog/2025/10/my-newest-post/

Including the date in the URL feels clunky, makes the link unnecessarily long, and can even discourage updates to older content because the original publication date is permanently embedded. A clean URL is better for SEO, easier for users to remember, and just looks more professional. The challenge was to have the best of both worlds: a tidy, dated folder structure for me, and clean, dateless URLs for my audience.


The Process & The Code 👨‍💻

Knowing Hugo’s power and flexibility, I was confident this would be a simple tweak in my configuration file. I immediately turned to the permalinks setting in my hugo.toml file. This feature is specifically designed to let you define custom URL patterns for different content sections.

Based on some examples I’d seen online, I added what I thought was the correct configuration. My logic was that these were “posts,” so I should define a permalink rule for posts.

Here’s the first change I made to my hugo.toml:

# My hugo.toml file

baseURL = "https://orioninsist.org/"
title   = "orioninsist"
# ... other configurations ...

[permalinks]
  posts = "/blog/:slug/"

The :slug variable is a Hugo placeholder for the URL-friendly version of the filename or the slug field in the front matter. This rule seemed perfect. It explicitly tells Hugo: “For any content in the ‘posts’ section, generate the URL by taking /blog/ and appending the slug.”

With the change saved, I stopped my local Hugo server and ran hugo server -D with confidence. I opened my browser, navigated to a post that was nested deep inside content/blog/2025/10/, and… my heart sank. The URL in the address bar was still the long, ugly, date-filled version.


Hitting The Wall 🧱

It didn’t work. Nothing had changed. I cleared my browser cache. I deleted the public and resources directories and rebuilt the site from scratch. Still nothing. The dated folder structure was being mirrored directly in my URLs, completely ignoring my permalinks rule.

This was incredibly frustrating. It felt like I was following the instructions perfectly, but Hugo was completely ignoring me. My mind started racing through the possibilities:

  • “Is it a bug in my version of Hugo?” (Unlikely, but possible).
  • “Is my theme, PaperMod, overriding the setting somehow?” (A definite possibility).
  • “Is there another, more obscure setting somewhere that’s taking precedence?”
  • “Am I just fundamentally misunderstanding how permalinks work?”

I spent the next hour or two digging through Hugo forums, Stack Overflow threads, and the official documentation again. I found countless examples of people using [permalinks], and they all looked just like mine. The frustration grew because the problem felt so simple, yet the solution was proving elusive. It’s moments like these that can completely derail your productivity. I was no longer writing content; I was deep in a configuration battle that I seemed to be losing.


The Breakthrough Moment ✨

After stepping away for a coffee and coming back with a clearer head, I decided to re-read the Hugo documentation on Permalinks, but this time, I read it word by word, as if I’d never seen it before. And there it was. A single, critical sentence that I had previously skimmed over.

The documentation explains that the keys in the [permalinks] configuration map directly to your content sections. A content section in Hugo is, by default, the top-level folder inside your content directory.

My content was located in content/blog/. Therefore, my content section was named blog.

My configuration file, however, was defining a rule for a section named posts:

[permalinks]
  posts = "/blog/:slug/" # <--- This was the problem!

I didn’t have a content/posts/ folder. My rule was being applied to a section that didn’t exist in my project. The solution was suddenly, blindingly obvious. The key in the [permalinks] block had to be blog, not posts.

I quickly changed that one single word in my hugo.toml file:

# The final, correct hugo.toml configuration

baseURL = "https://orioninsist.org/"
title   = "orioninsist"
# ... other configurations ...

[permalinks]
  blog = "/blog/:slug/" # <--- Corrected from 'posts' to 'blog'

I held my breath, saved the file, and restarted the Hugo server. I clicked the link to my test post, and there it was, in all its glory: http://localhost:1313/blog/my-newest-post/.

It worked. The relief was immense. All that frustration was caused by a single, five-letter word. It was a powerful reminder that in development, the devil is always in the details.


This entire experience reminded me of a fundamental principle in design and development, which is beautifully captured in Steve Krug’s classic book, “Don’t Make Me Think, Revisited: A Common Sense Approach to Web Usability”. While my problem was with my own developer experience, the goal was to improve the user experience by creating clean URLs. Krug’s book is an essential read for anyone who builds websites. It argues that a user should never have to puzzle over how to use a website. A good URL, just like a good button or navigation menu, shouldn’t make you think. It should just make sense. This book will change how you see your own projects. Amazon


Key Takeaways 📚

  1. 💡 Configuration Keys Are Not Arbitrary: In Hugo, the keys you use in your configuration files (like blog in [permalinks]) often map directly to the names of your folders. They aren’t just labels; they are identifiers. Always make sure your configuration matches your structure.
  2. ⚙️ Sections Are Your Top-Level Content Folders: Understanding Hugo’s concept of “sections” is fundamental. Whatever you name that first-level directory inside content (blog, projects, notes, etc.), that’s the section name you need to use in your hugo.toml for section-specific rules.
  3. 📚 When Stuck, Re-Read the Docs Slowly: When you’re debugging, it’s easy to skim documentation looking for a quick fix. But often, the solution is in a small detail you missed. Stepping back and reading the official documentation carefully and slowly is one of the most powerful debugging tools you have.

Thanks for Following ☕

I hope this detailed walkthrough of my struggle and solution helps someone else out there who is wrestling with Hugo configurations. It’s a small detail, but it makes a world of difference for maintaining a clean and scalable site.

☕ If you found this guide helpful, you can Buy Me a Coffee! Medium Etsy LinkedIn Read More

Have you ever spent hours debugging a problem that turned out to have a ridiculously simple solution? I’d love to hear your story in the comments!