# Audit your published site for problems

**URL:** <https://discourse.gohugo.io/t/audit-your-published-site-for-problems/35184>\
**Category:** tips & tricks\
**Created:** [October 17, 2021, 7:14pm UTC](https://discourse.gohugo.io/t/audit-your-published-site-for-problems/35184 "2021-10-17T19:14:24Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![jmooring](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.gohugo.io/jmooring/32/4214_2.png) [@jmooring](https://discourse.gohugo.io/u/jmooring)\
**Post date:** [October 17, 2021, 7:14pm UTC](https://discourse.gohugo.io/t/audit-your-published-site-for-problems/35184/1 "2021-10-17T19:14:24Z")

</div>

Last updated: 2023-11-10T21:41:24-08:00

This information is now included in the [Hugo documentation](https://gohugo.io/troubleshooting/audit/).

* * *

## Why

There are several conditions that can produce errors in your published site which are not detected during the build. Run this audit before your final build. Yes, I understand that this means building the site twice.

## The Audit

```bash
HUGO_MINIFY_TDEWOLFF_HTML_KEEPCOMMENTS=true HUGO_ENABLEMISSINGTRANSLATIONPLACEHOLDERS=true hugo && grep -inorE "<\!-- raw HTML omitted -->|ZgotmplZ|\[i18n\]|\(<nil>\)|(&lt;nil&gt;)|hahahugo" public/

```

Tested with GNU Bash 5.0 / GNU grep 3.4.

## Example Output

 ![image](https://canada1.discourse-cdn.com/flex036/uploads/gohugo/original/2X/b/b143b7f55a528c14daf3ee28349dc7a87066af34.png)

## Explanation

#### Environment Variables
`HUGO_MINIFY_TDEWOLFF_HTML_KEEPCOMMENTS=true`Retain HTML comments even if minification is enabled. This takes precedence over `[minify.tdewolff.html.keepComments]` in the site configuration. If you minify without keeping HTML comments when performing this audit, you will not be able to detect when raw HTML has been omitted.`HUGO_ENABLEMISSINGTRANSLATIONPLACEHOLDERS=true`Show a placeholder instead of the default value or an empty string if a translation is missing. This takes precedence over `enableMissingTranslationPlaceholders` in the site configuration.
#### Grep Options
`-i, --ignore-case`Ignore case distinctions in patterns and input data, so that characters that differ only in case match each other.`-n, --line-number`Prefix each line of output with the 1-based line number within its input file.`-o, --only-matching`Print only the matched (non-empty) parts of a matching line, with each such part on a separate output line.`-r, --recursive`Read all files under each directory, recursively, following symbolic links only if they are on the command line.`-E, --extended-regexp`Interpret PATTERNS as extended regular expressions.
#### Patterns
`<!-- raw HTML omitted -->`By default, Hugo strips raw HTML from your markdown prior to rendering, and leaves this HTML comment in its place.`ZgotmplZ`ZgotmplZ is a special value that indicates that unsafe content reached a CSS or URL context at runtime. For more information see https://pkg.go.dev/html/template.`[i18n]`This is the placeholder produced instead of the default value or an empty string if a translation is missing.`(<nil>)`This string will appear in the rendered HTML when passing a nil value to the `printf` function.`(&lt;nil&gt;)`Same as above when the value returned from the `printf` function has not been passed through `safeHTML`.`HAHAHUGO`Under conditions too complex to explain in this article, a rendered shortcode may include all or a portion of the string `HAHAHUGOSHORTCODE` in either uppercase or lowercase. This is difficult to detect in all circumstances because, depending on a variety of factors, the rendered shortcode may include `H`, `HA`, `HAH`, `HAHA`, `HAHAH`, `HAHAHU`, `HAHAHUG`, `HAHAHUGO`, etc. A case-insensitive search of the output for `HAHAHUGO` is likely to catch the majority of cases without producing false positives.
## Comments

The problems that this audit detects are surprisingly common in public sites created with Hugo. While you would have to view source in your browser to detect `<!-- raw HTML omitted ‐‐>` or `ZgotmplZ`, you can easily find instances of `HAHAHUGOSHORTCODE` with a [simple search](https://www.google.com/search?q=-site%3Agohugo.io+-site%3Agithub.com+-site%3Afossies.org+-site%3Abountysource.com+-site%3Aissueexplorer.com+HAHAHUGOSHORTCODE).

---

<div class="post-metadata">

**Author:** ![divinerites](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.gohugo.io/divinerites/32/12625_2.png) [@divinerites](https://discourse.gohugo.io/u/divinerites)\
**Post date:** [November 4, 2021, 3:53pm UTC](https://discourse.gohugo.io/t/audit-your-published-site-for-problems/35184/2 "2021-11-04T15:53:58Z")

</div>

Great tip !!

Is this audit could be a (to be done) hugo build option ?

---

<div class="post-metadata">

**Author:** ![alexandros](https://avatars.discourse-cdn.com/v4/letter/a/ecc23a/32.png) [@alexandros](https://discourse.gohugo.io/u/alexandros)\
**Post date:** [November 4, 2021, 6:31pm UTC](https://discourse.gohugo.io/t/audit-your-published-site-for-problems/35184/3 "2021-11-04T18:31:18Z")

</div>

> [@jmooring](#):
>
> `HAHAHUGO`
> 
> Under conditions too complex to explain in this article, a rendered shortcode may include all or a portion of the string `HAHAHUGOSHORTCODE` in either uppercase or lowercase. This is difficult to detect in all cicumstances because, depending on a variety of factors, the rendered shortcode may include `H` , `HA` , `HAH` , `HAHA` , `HAHAH` , `HAHAHU` , `HAHAHUG` , `HAHAHUGO` , etc. A case-insensitive search of the output for `HAHAHUGO` is likely to catch the majority of cases without producing false positives.

Sounds like an Easter egg. [guess someone is having fun behind the scenes, good for them…]

However unlike the other error strings, I’ve never seen `HAHAHUGO` despite debugging Hugo projects for countless hours in the past.

Perhaps because I prefer keeping shortcodes to a minimum.

Anyway thanks for the tip.

---

<div class="post-metadata">

**Author:** ![jmooring](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.gohugo.io/jmooring/32/4214_2.png) [@jmooring](https://discourse.gohugo.io/u/jmooring)\
**Post date:** [November 4, 2021, 6:56pm UTC](https://discourse.gohugo.io/t/audit-your-published-site-for-problems/35184/4 "2021-11-04T18:56:56Z")

</div>

From live sites…

 ![image](https://canada1.discourse-cdn.com/flex036/uploads/gohugo/original/2X/b/bac6cbd495a6946104e677e01b36a758601347d4.jpeg)

* * *

![image](https://canada1.discourse-cdn.com/flex036/uploads/gohugo/original/2X/f/f370115e5cb69ccbe23c702b90adde67b929fa7b.png)

* * *

![image](https://canada1.discourse-cdn.com/flex036/uploads/gohugo/original/2X/c/c399fb25ea1555f0818c764da155eba1b081eb37.png)

* * *

![image](https://canada1.discourse-cdn.com/flex036/uploads/gohugo/original/2X/3/32eace5648c1205641a782fd44f837f1f3e0f7a6.png)

And many, many more.

---

<div class="post-metadata">

**Author:** ![alexandros](https://avatars.discourse-cdn.com/v4/letter/a/ecc23a/32.png) [@alexandros](https://discourse.gohugo.io/u/alexandros)\
**Post date:** [November 4, 2021, 7:12pm UTC](https://discourse.gohugo.io/t/audit-your-published-site-for-problems/35184/5 "2021-11-04T19:12:07Z")

</div>

~~When was this added?~~

I am very surprised not having seen this before.

**EDIT**  
Never mind. It was added [3 years ago](https://github.com/gohugoio/hugo/blame/d966f5d08d7f75f1ae9acd94e292bf61de2adf0d/hugolib/shortcode_test.go#L436-L440)

---

<div class="post-metadata">

**Author:** ![jmooring](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.gohugo.io/jmooring/32/4214_2.png) [@jmooring](https://discourse.gohugo.io/u/jmooring)\
**Post date:** [November 16, 2021, 6:53pm UTC](https://discourse.gohugo.io/t/audit-your-published-site-for-problems/35184/6 "2021-11-16T18:53:09Z")

</div>

I’ve updated the audit to include a search for `(<nil>)` and its non-safe equivalent.

```nohighlight
{{ printf "%s" site.Params.does_not_exist }} --> %!s(&lt;nil&gt;)
{{ printf "%s" site.Params.does_not_exist | safeHTML }} --> %!s(<nil>)

```

---

<div class="post-metadata">

**Author:** ![cshoredaniel](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.gohugo.io/cshoredaniel/32/19684_2.png) [@cshoredaniel](https://discourse.gohugo.io/u/cshoredaniel)\
**Post date:** [November 24, 2021, 12:19am UTC](https://discourse.gohugo.io/t/audit-your-published-site-for-problems/35184/7 "2021-11-24T00:19:22Z")

</div>

FYI there is one gotcha with this. If you try and document this audit command in your content naively, then the audit will fail because the documentation will match the grep.

I don’t have a perfect solution, but for now I’m using this: (not really happy with it though):

Might need `set -o pipefail` to be set as well.

```bash
if test "$(grep -iIvrnE 'grep(.+(-- raw HTML omitted --|ZgotmplZ|hahahugo|\\\[i18n\\\])+)' "${OUTPUT_DIRECTORY}" | grep -iIoE '<\!-- raw HTML omitted -->|ZgotmplZ|hahahugo|\[i18n\]')" != ""; then echo "not ok"; exit 1; else echo "ok"; exit 0; fi

```

It may (or may not) be of interest to some that I have create GitHub Action that builds a Hugo site and runs this test.

> **[GitHub - danielfdickinson/hugo-action-build-audit: GitHub action to checkout,...](https://github.com/danielfdickinson/hugo-action-build-audit)**
>
> GitHub action to checkout, build, and audit a Hugo site - GitHub - danielfdickinson/hugo-action-build-audit: GitHub action to checkout, build, and audit a Hugo site

---

<div class="post-metadata">

**Author:** ![kaushalmodi](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.gohugo.io/kaushalmodi/32/2567_2.png) [@kaushalmodi](https://discourse.gohugo.io/u/kaushalmodi)\
**Post date:** [January 13, 2022, 2:42pm UTC](https://discourse.gohugo.io/t/audit-your-published-site-for-problems/35184/8 "2022-01-13T14:42:47Z")

</div>

Thanks for this writeup @jmooring!

I just added this to my Netlify’s bash build script:

```bash
set -euo pipefail # http://redsymbol.net/articles/unofficial-bash-strict-mode
IFS=$'\n\t'

# ..

site_audit () {
    audit_dir="./audit/"

    # https://discourse.gohugo.io/t/audit-your-published-site-for-problems/35184
    if ! HUGO_MINIFY_TDEWOLFF_HTML_KEEPCOMMENTS=true \
         HUGO_ENABLEMISSINGTRANSLATIONPLACEHOLDERS=true \
         hugo --destination "${audit_dir}"
    then
        echo "FAIL: hugo run failed."
        exit 1
    fi

    if grep -inorE "<\!-- raw HTML omitted -->|ZgotmplZ|\[i18n\]|\(<nil>\)|(&lt;nil&gt;)|hahahugo" "${audit_dir}"
    then
        echo "FAIL: Site audit. Review the contents of ${audit_dir}."
        exit 1
    else
        echo "PASS: Site audit"
        rm -rf "${audit_dir}"
    fi
    echo ""
}

# ..

site_audit

# ..

```

---

<div class="post-metadata">

**Author:** ![MatthewMcD](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.gohugo.io/matthewmcd/32/5578_2.png) [@MatthewMcD](https://discourse.gohugo.io/u/MatthewMcD)\
**Post date:** [August 17, 2022, 3:48pm UTC](https://discourse.gohugo.io/t/audit-your-published-site-for-problems/35184/9 "2022-08-17T15:48:56Z")

</div>

@jmooring Does Hugo on Windows support environment variables or would these settings have to be a separate configuration? I’d love to get this working locally before I push it to my build pipeline. Not sure what the “Windows equivalent commands” would be.

---

<div class="post-metadata">

**Author:** ![jmooring](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.gohugo.io/jmooring/32/4214_2.png) [@jmooring](https://discourse.gohugo.io/u/jmooring)\
**Post date:** [August 17, 2022, 3:58pm UTC](https://discourse.gohugo.io/t/audit-your-published-site-for-problems/35184/10 "2022-08-17T15:58:05Z")

</div>

> [@MatthewMcD](#):
>
> Does Hugo on Windows support environment variables

Yes. Something like:

```auto
set HUGO_FOO=wibble
set HUGO_BAR=wobble
hugo

```

---

<div class="post-metadata">

**Author:** ![MatthewMcD](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.gohugo.io/matthewmcd/32/5578_2.png) [@MatthewMcD](https://discourse.gohugo.io/u/MatthewMcD)\
**Post date:** [August 17, 2022, 3:59pm UTC](https://discourse.gohugo.io/t/audit-your-published-site-for-problems/35184/11 "2022-08-17T15:59:53Z")

</div>

Thanks, I’ll take this for a spin.

---

<div class="post-metadata">

**Author:** ![MatthewMcD](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.gohugo.io/matthewmcd/32/5578_2.png) [@MatthewMcD](https://discourse.gohugo.io/u/MatthewMcD)\
**Post date:** [August 17, 2022, 7:17pm UTC](https://discourse.gohugo.io/t/audit-your-published-site-for-problems/35184/12 "2022-08-17T19:17:27Z")

</div>

I love this topic! I get to learn more about Hugo and improve my own sites in the process. I develop on Windows and although there are tools for emulating Linux, I prefer PowerShell as I use it daily for my “real” job. Here is how I translated this process. Feedback is always welcome.

Although you can use environment variables in PowerShell I found it easier to create an “Audit” configuration:

```auto
enableMissingTranslationPlaceholders = true
[minify]
  [minify.tdewolff]
    [minify.tdewolff.html]
      keepComments = true

```

Then define the regex pattern (I added `\\u0026rsquo;` because that has been an issue for my migrated sites):

```auto
$regexPattern = '<\!-- raw HTML omitted -->|ZgotmplZ|\[i18n\]|\(<nil>\)|(&lt;nil&gt;)|hahahugo|\\u0026rsquo;'

```

Build Hugo with the `Audit` configuration:

```auto
hugo -e audit

```

Then get the errors using `Select-String`:

```auto
Get-ChildItem -Path .\public -Recurse | Select-String -Pattern $regexPattern -CaseSensitive:$false | Select Filename, LineNumber, Line, Path | Format-Table

```

![image](https://canada1.discourse-cdn.com/flex036/uploads/gohugo/original/3X/c/9/c99ff93ff44315d9b76d247989bb1bede3f7219d.png)

You can run `Select-String` with the `-List` parameter and only return the first instance of the error in the file:

```auto
Get-ChildItem -Path .\public -Recurse | Select-String -Pattern $regexPattern -CaseSensitive:$false -List | Select Filename, LineNumber, Line, Path | Format-Table

```

![image](https://canada1.discourse-cdn.com/flex036/uploads/gohugo/original/3X/5/e/5e93cb4c50c821b4a83fc13c91018543dec81848.png)

Finally, in the event you want to simply pass or fail you can assign the match results to a variable:

```auto
$lines = Get-ChildItem -Path .\public -Recurse | Select-String -Pattern $regexPattern -CaseSensitive:$false

```

If `$lines.Count -eq 0` you don’t have any errors.

---

<div class="post-metadata">

**Author:** ![steppa](https://avatars.discourse-cdn.com/v4/letter/s/f0a364/32.png) [@steppa](https://discourse.gohugo.io/u/steppa)\
**Post date:** [December 10, 2022, 6:53pm UTC](https://discourse.gohugo.io/t/audit-your-published-site-for-problems/35184/13 "2022-12-10T18:53:52Z")

</div>

I saved this as a bash script and run it with Git and it helped me solve a few issues. Sweet!
