-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Description
Description
When using {{ dict ... | jsonify | safeHTML }} inside <script type="application/ld+json">, the output is double-encoded JSON — a JSON string literal wrapped in quotes instead of a JSON object:
// Actual output (broken):
"{"@context":"https://schema.org","@type":"WebSite"}"
// Expected output:
{"@context":"https://schema.org","@type":"WebSite"}
Root cause
Go's html/template is context-aware. Inside <script> tags, it applies JS-context escaping regardless of the type attribute. safeHTML returns template.HTML which bypasses HTML escaping, but does NOT bypass JS-context escaping. The template engine treats the content as a JS value and wraps it in quotes with escaped inner quotes.
This is not a Hugo bug — it's standard html/template behavior. But it's a documentation gap that affects every Hugo theme using JSON-LD structured data.
Correct pattern
{{/* Build the entire <script> tag as a string, bypassing JS-context */}}
{{ $data := dict "@context" "https://schema.org" "@type" "WebSite" "name" .Title }}
{{ printf `<script type="application/ld+json">%s</script>` ($data | jsonify) | safeHTML }}
Incorrect patterns (commonly used)
{{/* BROKEN: safeHTML does not bypass JS-context escaping inside <script> */}}
<script type="application/ld+json">
{{ dict "@context" "https://schema.org" "@type" "WebSite" | jsonify | safeHTML }}
</script>
{{/* ALSO PROBLEMATIC: safeJS has known issues with partials (#8001) */}}
<script type="application/ld+json">
{{ dict "@context" "https://schema.org" "@type" "WebSite" | jsonify | safeJS }}
</script>
Impact
This silently breaks ALL structured data (JSON-LD) on affected sites. Google Search Console shows 0 rich results, and site owners assume it's a configuration issue. The broken JSON is syntactically valid (it's a JSON string), so it doesn't cause build errors.
Related issues
- Invalid escape code in ld+json script tag hugo#6524 — Invalid escape code in ld+json script tag (closed as dup of hugo converts "+" into "\x2b", causing ld+json parsing failure hugo#6695)
- hugo converts "+" into "\x2b", causing ld+json parsing failure hugo#6695 —
+converted to\x2bin ld+json (fixed in v0.72) - Jsonify is escaping characters hugo#7229 — jsonify escaping characters (closed)
- safeJS improperly escaping < character when applied to partials hugo#8001 — safeJS improperly escaping
<in partials (closed)
None of these document the double-encoding mechanism or provide the correct pattern.
Request
Add a canonical JSON-LD example to the encoding.Jsonify documentation page showing the printf + safeHTML pattern. This would prevent the most common structured data bug in Hugo themes.
Environment
- Hugo v0.156.0
- macOS,
hugo --minify - Reproducible on all versions using
html/template