This article was last updated on October 10, 2023, 8 months ago. The content may be out of date.

Hugo is a static site generator. However sometimes we want to serve dynamic contents. We can embed javascript to achieve what we want, but why not use templates when they’re more convenient?

Caddy Template Engine

Caddy comes with a powerful template engine that is based on golang standard library. Some of the sites (including Caddy’s official website) are built this way.

The list of template functions/actions can be viewed here. In addition, caddy also support all Sprig functions.

Choosing Template Delimiters

Because generated sites may contain the default delimiters ({{ and }}), we need to change them. This site uses custom html tags like <x-hugo> and </x-hugo>.


Some Hugo themes will generate json-ld element. Usually they will strip html elements from the post content. Because caddy templates will try to parse the whole document, if we use any other types of delimiters, they may cause errors.

During testing we chose delimiters like {{hugo and hugo}}, the rendered post content is fine, but caddy also tried to parse the linking data in the post metadata. There are some hacks to fix this problem. Choosing a different type of delimiters is easier and cleaner.

Defining a Hugo Shortcode

Hugo shortcode is a simple snippet inside a content file that Hugo will render using a predefined template.

We can define the shortcode like this:

<x-hugo> {{ range .Params }} {{ . }} {{ end }} </x-hugo>

This way we can handle any number of arguments.

Caddy Templates Examples


Replace {{ and }} with your own delimiters.

Below are several examples showing how to use caddy templates.

  • Random UUID: {{ uuidv4 }}8ebc5d36-0d38-4abd-9cf3-5f86cafa7be4
  • Client address: {{ placeholder "http.vars.client_ip" }}
  • Client user agent: {{ .Req.UserAgent }}CCBot/2.0 (
  • Client HTTP version: {{ .Req.Proto }}HTTP/1.1

A complex example testing if the client supports zstd content encoding:

{{ $ce := placeholder "http.request.header.Accept-Encoding" }}
{{ $sz := false }}
{{ $ces := splitList "," $ce }}
{{ range $ce := $ces }}
    {{ $cq := splitList "," $ce }}
    {{ $c := index $cq 0 | trim }}
    {{ if eq $c "zstd" }}
        {{ $sz = true }}
        {{ break }}
    {{ end }}
{{ end }}

{{ if $sz }}
Your browser supports zstd.
{{ else }}
Your browser doesn't support zstd.
{{ end }}


Your browser doesn’t support zstd.