There is no problem not worth solving, and no technology not worth learning!

Getting Started with Go Template Engine

Go Templates

Getting Started with Go Template Engine

(1) Getting Started with Go Template Engine


This series of articles records the author’s experience and practical experience in learning the Go standard library. This article is from the built-in template engine of Go Html/Template And text/template Start with, take you to quickly grasp the core concepts and practical usage, and intersperse the developer perspective of PHP to Go.

Introduction: From PHP Mix to Go Template Separation

If you go from PHP to Go like me, you will definitely have a complex feeling for the words ‘template engine’. PHP itself is a template engine – you can embed in HTML at any time <?php ?> labels, mixed with business logic. Laravel’s Blade, ThinkPHP’s ThinkTemplate, etc. are actually further encapsulation and constraints on this native mixing ability.

And the Go language has come another way. Instead of built-in the template system to the language syntax level, it provides two separate template engine packages in the standard library:text/template And Html/Template. This means that template rendering is explicit, controllable, and type-safe.

This design concept is very Go:Make things simple and clear, and leave complex choices to developers.

1. What is Go’s template engine?

The template engine officially provided by Go is essentially a Data-driven text generator. It combines ‘template’ (text with placeholders and instructions) with ‘data’ (an arbitrary structure, map, variable, etc.) to generate the final output content.

In web development, the template engine is as follows:

  1. Define a template file: create one tmpl.html file, which embeds instructions (such as {{ . }}).
  2. Parsing template files: call template.parseFiles() Load templates.
  3. Incoming data execution: call t.execute() Generate the final output.

📁 Template file suffix recommendation

The Go language has no mandatory requirements for template file suffixes. But starting with project maintainability and cross editor compatibility, the community has gradually formed a recommendation convention:

SuffixExplanation
.gotmplbest recommendation. Go official language server gopls Default recognition, clear semantics, good cross IDE consistency.
.tmplHistory is commonly used, but some editors need to manually configure syntax highlighting.
.gohtmlSuitable for HTML output scenarios, friendly to front-end tools.

It is recommended to use new projects in a unified manner .gotmpl. The example of this article is a common tutorial for compatibility, still use .html Suffix, you can adjust it yourself according to the project specification.

Go’s template engine is between ‘no business logic’ and ’embedded business logic’ – it supports basic control structures such as conditional judgment and looping, but does not encourage the insertion of complex business logic into the template. This is a pragmatic trade-off.

Two, two template engines:text/template vs Html/Template

Go provides two template packages, theyShare the same set of APIs and syntax, but the positioning is completely different:

Bag nameApplicable scenariosSafety Features
text/templatePlain text output (profile, script, code generation, log, etc.)No automatic escape, output as is
Html/TemplateHTML output in web applicationscontext perceptionAutomatic escape to prevent XSS attacks

The core principles are:output html to use Html/Template, the output plain text is used text/template, do not mix.

First knowledge Html/Template: a minimal example

Create tmpl.html file:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Go 模板示例</title>
</head>
<body>
    <!-- {{ . }} 是最基本的动作,输出传入的数据 -->
    <h1>{{ . }}</h1>
</body>
</html>

Go server code:

package main

import (
    "html/template"
    "net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
    // 1. 解析模板文件,返回 *Template 指针
    t, _ := template.ParseFiles("tmpl.html")
    // 2. 执行模板,将数据写入 ResponseWriter
    t.Execute(w, "Hello, Go 模板引擎!")
}

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8080", nil)
}

Save the above code as main.go, and create it in the sibling directory tmpl.html file (contents are shown before). Then execute:

go run main.go

The terminal will output similar information (no error is reported to indicate that the service started successfully):

# 无输出表示成功,或者可以添加 log.Println 主动提示

At this point, open your browser and enter:

http://localhost:8080

Press Enter, and the browser page will display:

Hello, Go Template Engine!

If you see this text on your page, it means that your first Go template engine example has run successfully. as shown in Figure 1

If you see this text on your page, it means that your first Go template engine example has run successfully. as shown in Figure 1

Handle errors with must

The above example deliberately omits error handling (t, _ := ...), which is not safe in the production environment. The standard library provides a convenient function template.must: If there is an error when parsing the template, it will directly touch the send panicto avoid subsequent calls execute appear nil pointer.

t := template.Must(template.ParseFiles("tmpl.html"))
// 现在可以放心地调用 t.Execute

This design is very suitable for init() Or main() The global initialization phase is used.

3. In-depth understanding of the implementation mechanism of templates

3.1 Multiple ways to parse templates

template The package provides multiple functions to parse the template source:

  • Template.parseFiles(filename ...string): Parses one or more template files. If multiple files are passed in, the returned *Template Withfirst filenameAs a template name, the rest of the files are stored as association templates.
  • template.parseGlob(pattern string): Use wildcards to parse templates in batches, e.g. template.parseGlob('templates/*.html').
  • template.new(name).parse(text): Create a named template first, and then parse from the string.

3.2 Two ways to execute a template

When there are multiple templates in the template collection, you need to distinguish the execution method:

  • t.execute(w, data): Execute the collection in the collection default template(usually through parsefiles the first file passed in).
  • t.executeTemplate(w, name, data): according to the templateNameExecute the specified template.
// 示例:ParseFiles 传入了两个文件,但默认模板是第一个
t := template.Must(template.ParseFiles("layout.html", "content.html"))
// 执行 layout.html,而非自动执行为第一个
t.ExecuteTemplate(w, "layout.html", data)

3.3 Template naming and scope

Go’s template system also has a key point:*Template.Template Essentially a named collection of templates, not a single template.

// 创建并添加多个命名模板
t := template.New("base")
t = template.Must(t.Parse(`{{define "header"}}头部内容{{end}}`))
t = template.Must(t.Parse(`{{define "footer"}}底部内容{{end}}`))

// 执行任意命名模板
t.ExecuteTemplate(os.Stdout, "header", nil)

This design provides the basis for template reuse and layout inheritance.

4. Basics of template grammar (open preview)

The first article will take you to familiarize yourself with the two most core grammar elements, and the follow-up article will be expanded in depth.

4.1 Action (Action):{{ }}

All template instructions are written in { And }} , called ‘action’. There can be spaces inside and outside the action, and the Go template parser has a certain tolerance for spaces.

  • {{ . }}: Output the current data context (point number).
  • {{ .fieldname }}: Access the fields of the data structure, the field nameMust be capitalized(export).
  • {{ .method }}: You can call the method of the recipient with the current value.

4.2 Variables and pipes:{{ $var := .field }}

Available in Go templates $ Variables at the beginning to store intermediate results and concatenate multiple operations in series through pipes:

{{ $author := .Author.Name }}
{{ $author | printf "作者:%s" }}

The pipe style is similar to the Unix pipeline, and it can be transformed in series from left to right.

4.3 Notes:{{/* Comments */}}

Template annotation does not appear in the final output, and is suitable for the logical description of the template.

{{/* 这里是一段注释,不会输出到 HTML */}}

4.4 Go template syntax from PHP perspective

Scenesphp (blade)go template
output variable{{ $name }}{{ .name }}
Cycle@foreach($users as $user){{ range .users }}
Condition@if($active){{ if .active }}
Note{{-- comment --}}{{/* Note */}}
Inherit@extends(Layout)Use {{ define }} + {{ template }}

Variable access for Go templates is requiredCapitalize the first letter(due to the limitation of the reflection mechanism), and this constraint does not exist in PHP. This is often the easiest pit for beginners in PHP to go to step on.

Five,Html/Template security mechanism

5.1 Automatic escape: use security as the default behavior

Html/Template The most powerful feature is Automatic escape of context perception. When the template engine is parsed, it analyzes each {{ .field }} The location (in the HTML tag, in the attribute value, JavaScript code block, CSS style block), and then automatically select the most suitable escape strategy.

data := struct {
    UserInput string
}{
    UserInput: `<script>alert('XSS')</script>`,
}
t.Execute(w, data)

If UserInput is placed in ordinary HTML tags (such as <div>{{ .userinput }}</div>), the output will be escaped as:

&lt;div>&amp;lt;script&amp;gt;alert(&amp;#39;XSS&amp;#39;)&amp;lt;/script&amp;gt;&lt;/div>

Scripts don’t execute, fundamentally defending against XSS attacks.

5.2 When to bypass escaping:template.html Types of

If you really need to output the original HTML (such as the content generated by the rich text editor in the background), you can use template.html type. But the premise is that you have fully trusted the content and have already purified it as necessary:

import "html/template"

type PageData struct {
    SafeHTML template.HTML  // 原样输出,不会转义
    RawText  string          // 会被自动转义
}

data := PageData{
    SafeHTML: template.HTML("<strong>加粗文字</strong>"),
    RawText:  "<strong>会被转义</strong>",
}

5.3 Security enhancements for Go 1.26

As of the time of writing, Go 1.26 Html/Template There are security enhancements for a particular Meta refresh scenario:

CVE-2026-27142: Insert the URL to <meta http-equiv='refresh' content='url=...'> Some boundary conditions may result in an escape failure with XSS risk when in the Content property. This issue has been fixed in 1.25.8 and 1.26.1.

If you are using Go 1.26.0 (without 1.26.1), it is recommended to upgrade to the latest patch version. If you cannot upgrade due to special reasons, you can set the GoDebug environment variable HtmlMetaContentUrlescape=0 The escape mechanism is temporarily disabled.

5.4 Comparison of PHP: XSS protection similarities and differences

Compare the dimensionphpgo (Html/Template)
automatic escapeNeed to call manually HtmlSpecialChars()Default automatic escaping (context-aware)
risk of misuseDevelopers may forget to callDefault security, security is the norm rather than choice
Bypass safety{!! $HTML !!} (blade)template.html type tag
credibility requirementsDevelopers need to be vigilant at all timesType System Forced Security Contract

The most essential difference between Go’s approach is:Security is not the option of ‘remember to do’, but the default behavior of the engine.

6. Summary

Today we start from scratch and systematically sort out the core concepts of the Go language’s built-in template engine:

  1. Two engines:text/template(plain text) and Html/Template(Secure HTML) – The API is the same, the scenarios are different.
  2. three-step process: Definition → parse (parsefiles)→ execute (execute), looping back and forth.
  3. grammar basics:{{ . }} access data,{{ $var }} define variables,{{/* Note */}} assist understanding.
  4. Security Advantage: Context perception is automatically escaped, fundamentally preventing XSS, new thinking necessary for PHP developers.

Need long-term technical maintenance or remote troubleshooting?

I am a PHP / Go backend engineer with 15+ years of experience, focused on existing system maintenance, bug fixing, performance optimization, server troubleshooting, WordPress maintenance, and small feature iterations.

If your project is facing any of the following issues, we can start with a small troubleshooting task first:

  • ✅ PHP / Laravel / Yii2 legacy systems without active maintenance
  • ✅ Go / Gin backend APIs that need troubleshooting or optimization
  • ✅ Slow, broken, or unstable WordPress websites
  • ✅ Nginx / MySQL / Redis / Linux server issues
  • ✅ CDN / Cloudflare / DNS / HTTPS configuration problems
  • ✅ Long-term remote technical support or part-time maintenance

More details: About Me & Collaboration

WeChat: 13980074657
Email: shuijingwanwq@gmail.com
Telegram: @shuijingwan
GitHub: https://github.com/shuijingwan

评论

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.