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/TemplateAndtext/templateStart 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:
- Define a template file: create one
tmpl.htmlfile, which embeds instructions (such as{{ . }}). - Parsing template files: call
template.parseFiles()Load templates. - 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:
| Suffix | Explanation |
|---|---|
.gotmpl | best recommendation. Go official language server gopls Default recognition, clear semantics, good cross IDE consistency. |
.tmpl | History is commonly used, but some editors need to manually configure syntax highlighting. |
.gohtml | Suitable 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 name | Applicable scenarios | Safety Features |
|---|---|---|
text/template | Plain text output (profile, script, code generation, log, etc.) | No automatic escape, output as is |
Html/Template | HTML output in web applications | context 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

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*TemplateWithfirst 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 throughparsefilesthe 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
| Scenes | php (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:
<div>&lt;script&gt;alert(&#39;XSS&#39;)&lt;/script&gt;</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 dimension | php | go (Html/Template) |
|---|---|---|
| automatic escape | Need to call manually HtmlSpecialChars() | Default automatic escaping (context-aware) |
| risk of misuse | Developers may forget to call | Default security, security is the norm rather than choice |
| Bypass safety | {!! $HTML !!} (blade) | template.html type tag |
| credibility requirements | Developers need to be vigilant at all times | Type 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:
- Two engines:
text/template(plain text) andHtml/Template(Secure HTML) – The API is the same, the scenarios are different. - three-step process: Definition → parse (
parsefiles)→ execute (execute), looping back and forth. - grammar basics:
{{ . }}access data,{{ $var }}define variables,{{/* Note */}}assist understanding. - Security Advantage: Context perception is automatically escaped, fundamentally preventing XSS, new thinking necessary for PHP developers.