v0.161.0

gohugoio/hugov0.161.0Apr 28, 2026by bep

AI Summary

Introduces nested CSS variables, a flexible slice-based permalinks configuration, and a new filename identifier scheme for content and layout files.

Key Highlights

  • Nested vars support in CSS/Sass using `hugo:vars`
  • Slice-based permalinks config using PageMatcher target
  • Flexible filename identifiers (role_, version_, etc.)
  • Harden Node tool execution with --permission flag

New Features

  • Nested vars in CSS/Sass
  • Slice-based permalinks
  • Flexible filename identifiers

Full Release Notes

This release contains two security hardening fixes:

* We now run the Node tools PostCSS, Babel and TailwindCSS, by default, with the `--permission` flag with the permissions defined in [security.node.permissions](https://gohugo.io/configuration/security/). This means that you need Node >= 22 installed and that `css.TailwindCSS` now requires that the Tailwind CSS CLI must be installed as a Node.js package. The [standalone executable](https://github.com/tailwindlabs/tailwindcss/releases/latest) is no longer supported
* We have made the defaults in [security.http.urls](https://gohugo.io/configuration/security/#httpurls) more restrictive.

But there are some notable new features, as well:

## Nested vars support in css.Build and css.Sass

A practical example in `css.Build` would be to have something like this in `hugo.toml`:

```toml
[params.style]
    primary    = "#000000"
    background = "#ffffff"
    [params.style.dark]
        primary    = "#ffffff"
        background = "#000000"
```
And in the stylesheet:

```css
@import "hugo:vars";
@import "hugo:vars/dark" (prefers-color-scheme: dark);

:root {
  color-scheme: light dark;
}
```

## Slice-based permalinks config

The `permalinks` configuration is now much more flexible (the old setup still works). It uses the same [target](https://gohugo.io/configuration/cascade/#target) matchers as in the `cascade` config, meaning you can now do:

```yaml
permalinks:
  - target:
      kind: page
      path: "/books/**"
    pattern: /books/:year/:slug/
  - target:
      kind: section
      path: "/{books,books/**}"
    pattern: /libros/:sections[1:]
  - target:
      kind: page
    pattern: /other/:slug/
```
The above example isn't great, but it at least shows the gist of it.

## A more flexible scheme for identifiers in filenames

What we had before was e.g. `content/mypost.en.md` which told Hugo that the content files was in English. With the new setup you could also name the file `content/mypost._language_en_.md`. This alone doesn't sound very useful, but this allows you to use more prefixes:

| Prefix          | Description   | Relevant for              |
| --------------- | ------------- | ------------------------- |
| language_       | Language      | Content and layout files. |
| role_           | Role          | Content and layout files. |
| version_        | Version       | Content and layout files. |
| outputformat_   | Output format | Layout files.             |
| mediatype_      | Media type    | Layout files.             |
| kind_           | Page kind     | Layout files.             |
| layout_         | Layout        | Layout files.             |

## All Changes

* langs/i18n: Fix translation lookup when using language variants 72b85d5f @jmooring #7982 
* create: Fix non-deterministic conflict detection in hugo new content 6436deb3 @jmooring #12602 #12786 #14112 #14769 
* commands: Fix environment isolation for configuration settings 1eea9fba @jmooring #14763 
* Fix filename dimension identifiers (_role_X_, _version_X_) to replace mount config 8d6145f3 @bep #14756 
* Fix it so we never auto-fallback to page resources in other roles/versions 97477242 @bep #14749 #14752 
* css: Support nested hugo:vars/<name> imports 7622dd86 @bep #14705 
* github: Update GitHub actions versions 0814059b @bep #14810 
* hugolib: Do not render aliases if the page is not rendered 8920d56e @jmooring #14807 
* langs/i18n: Improve default content language fallback 633cc772 @jmooring #14243 
* helpers: Remove unused code 4c40c6d5 @bep 
* common/constants: Remove unused consts d2594db6 @bep 
*  common/paths: Remove unused code ab2de51e @bep 
* tests: Update Ruby setup action to v1.305.0 75f61832 @jmooring 
* langs: Use Language.Locale as primary localization key 1b7495bc @jmooring #9109 
* config/security: Add "! " negation to Whitelist, harden default http.urls 79f030be @bep #14792 
* Harden Node tool execution with --permission flag a54c398b @bep #7287 
* tpl/collections: Honor the Eqer interface in where comparisons f5fce935 @bep #14777 
* modules: Ignore non-require blocks in go.mod rewrite 4169c1f7 @bep #14783 
* Replace the concurrent map with an identical upstream version 7574e35b @bep 
* Add slice-based permalinks config with PageMatcher target 017a7cd6 @bep #14744 
* commands: Add missing import e3413d92 @bep 
* Revert "common/hugo: Deprecate extended and extended_withdeploy editions" b01cc147 @bep #14771 
* Adjust the SECURITY.md slightly 8ee19ff9 @bep 
* resources/page: Add passing test for Issue #14325 0d58e428 @jmooring 
* Add a more flexible filename identifier scheme that also allows setting roles and versions (#14754) ce2a156a @bep #14750 
* common/hugo: Deprecate extended and extended_withdeploy editions a17bdbc5 @jmooring #14696 
*  parser/pageparser: Add a parser fuzz test 8f94d65c @bep 
* Replace deprecated .Site.Sites/.Page.Sites with hugo.Sites intests 90d8bf34 @bep 
* agents: Add a note about having the issue ID in test names bbb42b5a @bep 
* build(deps): bump github.com/getkin/kin-openapi from 0.135.0 to 0.137.0 d4ae662d @dependabot[bot] 
* build(deps): bump github.com/mattn/go-isatty from 0.0.21 to 0.0.22 9ede5fb9 @dependabot[bot] 
* build(deps): bump github.com/tdewolff/minify/v2 from 2.24.12 to 2.24.13 833a878e @dependabot[bot] 
* build(deps): bump github.com/magefile/mage from 1.17.1 to 1.17.2 4c03129f @dependabot[bot] 
* deps: Upgrade github.com/bep/imagemeta v0.17.1 => v0.17.2 080970bc @bep 
* build(deps): bump github.com/aws/aws-sdk-go-v2/service/cloudfront (#14789) 896bc89a @dependabot[bot] 
* build(deps): bump github.com/mattn/go-isatty from 0.0.20 to 0.0.21 (#14788) 100dde53 @dependabot[bot] 
* build(deps): bump github.com/bep/mclib (#14787) bdebb797 @dependabot[bot] 
* build(deps): bump google.golang.org/api from 0.267.0 to 0.276.0 52123ae2 @dependabot[bot] 
* build(deps): bump github.com/aws/aws-sdk-go-v2 from 1.41.5 to 1.41.6 38b8afdc @dependabot[bot] 
* build(deps): bump github.com/getkin/kin-openapi from 0.134.0 to 0.135.0 (#14781) 92766600 @dependabot[bot] 
* build(deps): bump github.com/bep/goportabletext from 0.1.0 to 0.2.0 (#14779) 790f4084 @dependabot[bot] 
* build(deps): bump golang.org/x/image from 0.38.0 to 0.39.0 (#14780) de6955ba @dependabot[bot] 
* deps: Upgrade github.com/bep/imagemeta v0.17.0 => v0.17.1 (#14775) a77bd527 @bep #14758 
* build(deps): bump golang.org/x/tools from 0.43.0 to 0.44.0 547ab29c @dependabot[bot] 
* build(deps): bump github.com/evanw/esbuild from 0.27.4 to 0.28.0 9a5c7e0d @dependabot[bot] 
* build(deps): bump github.com/aws/aws-sdk-go-v2 from 1.41.1 to 1.41.5 6613b08e @dependabot[bot] 
* build(deps): bump github.com/pelletier/go-toml/v2 from 2.2.4 to 2.3.0 582c26ef @dependabot[bot] 
* build(deps): bump github.com/tdewolff/minify/v2 from 2.24.11 to 2.24.12 a4f2a8a5 @dependabot[bot]