When you write {{.Title}} in a Go html/template, Go does not print the string — it prints a context-aware, escaped version. That automatic escaping is one of the best security features in the language, and it is exactly where hand-rolled template concatenation goes wrong.

What auto-escaping does for you

Go's template engine knows the context of every value:

  • Inside {{.Title}} in body text, it HTML-escapes (&, <, >, quotes).
  • Inside href="{{.URL}}", it escapes URL-hostile characters.
  • Inside onclick="...", it escapes for a JavaScript context.

The result: if user content contains <script>alert(1)</script>, it renders as harmless text instead of executing. You do not need a sanitizer for template output — the escaping is structural.

The mistake it prevents

The classic bug is building HTML by string concatenation:

html := "<div>" + userInput + "</div>"

One <script> in userInput and you have an XSS hole. With html/template, that same value is escaped automatically. This is why "just concatenate strings" is not a template.

Where it gets in the way — and what to do

  • You want raw HTML. Use template.HTML deliberately, and only for trusted content. The site's markdown renderer sanitizes with a whitelist first, then hands the result to the template as trusted HTML.
  • You want to inject a known-safe snippet. Same answer — wrap it explicitly. The moment you reach for template.HTML, you are telling Go "I take responsibility for this being safe."

The rule I follow

Default to auto-escaping. Escape by construction, sanitize at the boundary, and never concatenate HTML strings. Go's template engine gives you the safe default for free — the only skill is not disabling it casually.