根本原因是Fiber仅在engine初始化时root路径下查找模板,而非当前工作目录或main.go所在目录;需用绝对路径、embed.FS或确保相对路径与执行位置一致。

模板文件路径必须和engine初始化时的root参数对齐
报错 render: template index not found 的根本原因,不是文件不存在,而是 Fiber 没在你预期的位置找。它只认 engine := html.New("./views", ".html") 里传入的第一个参数(root 路径),然后拼上模板名 + 后缀去读取。
常见错误是项目结构里写了 /views/index.html,但启动命令在 /gogo 目录下执行 go run main.go,而 ./views 是相对于当前工作目录,不是相对于 main.go 所在目录。
- 开发时用绝对路径最稳:
html.New(filepath.Join(os.Getenv("PWD"), "views"), ".html")或直接写死html.New("/abs/path/to/views", ".html") - 上线建议用
embed.FS(Go 1.16+):html.NewFileSystem(http.FS(templates), ".html"),其中templates是embed.FS变量 - 别把模板放
./public或./static下——Fiber 的html.New不会自动扫描子目录,除非你显式调用ParseGlob或ParseFiles
渲染前必须确保 Content-Type 和状态码已设置
c.Render("index", fiber.Map{"Title": "Hello"}) 看似简单,但底层仍是写入 http.ResponseWriter。如果没设 Content-Type,浏览器可能把 HTML 当纯文本显示;如果 handler 中提前写了 header 或 status,Render 会 panic。
- 不用手动设
Content-Type:Fiber 的html引擎默认会在Render时写text/html; charset=utf-8,前提是没被中间件或前置逻辑覆盖 - 检查是否有中间件调用了
c.Status()或c.Set()修改了 header;一旦ResponseWriter已写过 header,Render就会失败 - 调试时加一行
fmt.Printf("header written? %v\n", c.Response().Committed),true表示 header 已发,不能再Render
嵌套模板(layout)要靠 {{template}} + 正确传参
Fiber 自带的 html 引擎支持 {{define}} 和 {{template}},但容易卡在数据上下文丢失。比如 layout.html 里写了 {{template "content" .}},但 content.html 里 {{.Title}} 渲染为空,问题不在模板语法,而在传参方式。
- 主模板中用
{{template "content" .}}(带点),才能把当前作用域完整传进子模板;写成{{template "content" nil}}或漏掉点,子模板就拿不到数据 - 子模板不能依赖全局变量——
fiber.Map传进来的数据只在首次Render时绑定,{{template}}不会自动继承外层变量 - 如果 layout 需要额外字段(如
.SEO),得在主模板里先{{with .SEO}}{{template "seo-tags" .}}{{end}},而不是指望子模板自己从根对象取
HTML 内容不转义必须用 safeHTML,且仅限可信来源
模板里写 {{.Body}},输出的是转义后的字符串(<p>Hello</p>),页面显示源码而非段落。这不是 bug,是 html/template 的安全机制在起作用。
- 只有明确可信的 HTML 才能绕过转义:
{{.Body | safeHTML}}(注意大小写,不是safehtml或htmlSafe) -
safeHTML是内置函数,不需要 import 或注册;但传入值类型必须是template.HTML,否则会 panic - 别用
strings.ReplaceAll拼接 HTML 字符串再塞进模板——这等于主动绕过所有安全防护,XSS 风险极高 - 如果内容来自用户输入、数据库或第三方 API,一律不要用
safeHTML;宁可用template.HTML包装前做白名单过滤
html.New 初始化后,模板树就固定了;后续改文件名或移动目录不会自动重载,必须重启进程。而 {{.Name}} 渲染为空时,90% 是结构体字段没导出(小写开头),不是模板写错了。


















