Iris 的 404 页面必须显式注册 OnErrorCode(iris.StatusNotFound, handler),它不参与路由匹配、不受中间件影响,专用于兜底未匹配请求;需根据 Accept 头或 IsAjax() 区分返回 JSON 或 HTML,并注意静态文件 404 不触发该处理器。

直接注册 OnErrorCode(iris.StatusNotFound) 即可
Iris 不会自动 fallback 到某个默认 HTML 文件,404 页面必须显式注册处理函数。只要调用一次 app.OnErrorCode(iris.StatusNotFound, handler),后续所有未匹配路由的请求(包括通配路由前漏掉的、静态资源路径错误、拼写错误等)都会进入这个 handler。
注意:它和普通路由是分离的,不参与路由树匹配,也不受中间件顺序影响——只要最终走到“找不到路由”这一步,就触发它。
OnErrorCode 里返回 HTML 还是 JSON 取决于客户端 Accept 头
你不能假设所有 404 都该返回 HTML;API 请求期望 JSON,浏览器请求才要 HTML。建议在 handler 中做简单判断:
- 检查
ctx.GetHeader("Accept")是否包含text/html - 或者更稳妥地,用
ctx.IsAjax()判断是否为 XHR 请求 - 前端 SPA 场景下,若当前路径以
/api/开头,应优先返回 JSON 错误体,避免把 404 当成 HTML 渲染
示例逻辑:
app.OnErrorCode(iris.StatusNotFound, func(ctx iris.Context) {
if ctx.IsAjax() || strings.HasPrefix(ctx.Request().URL.Path, "/api/") {
ctx.JSON(iris.Map{"error": "not found", "path": ctx.Request().URL.Path})
return
}
ctx.ServeFile("./dist/404.html")
})
别把 404 处理器和通配路由搞混
常见错误是以为配了 /{path:path} 就不用管 404——其实相反:通配路由本身会吞掉所有路径,导致真正该 404 的请求(比如 /favicon.ico、/robots.txt、/static/missing.js)也返回 index.html,掩盖了真实问题。
正确做法是:
- 先注册具体路由(
app.Get("/api/users", ...)) - 再注册通配路由(
app.Handle("GET", "/{path:path}", ...)),但内部用strings.HasPrefix排除/api/、/static/、/assets/等路径 - 最后注册
OnErrorCode(iris.StatusNotFound),兜底处理那些被通配规则放过、但又没命中任何静态文件的请求
否则你会看到:访问 /nonexistent.css 返回 index.html + 200,而不是 404 —— 这会让前端开发者误判资源加载失败原因。
静态文件 404 和路由 404 是两回事
如果你启用了 app.HandleDir 或 app.StaticWeb,它们内部有自己的 404 逻辑,不会走 OnErrorCode。也就是说,当用户请求 /static/logo.png 而文件不存在时,Iris 默认返回 plain text “Not Found”,且状态码是 404,但这个过程完全绕过你的 OnErrorCode 注册函数。
想统一控制,有两个选择:
- 禁用自动静态服务,改用
app.Handle("GET", "/static/{file:path}", ...)手动处理,并在内部调用ctx.ServeFile+os.Stat检查存在性,不存在时显式调用ctx.StatusCode(404)触发你的全局 404 handler - 保持
StaticWeb,但在其目录下放一个404.html,并配置 Web 服务器(如 Nginx)在 404 时重定向或响应该文件——Iris 本身不提供静态服务层的 404 模板能力
最容易被忽略的一点:本地开发时 curl http://localhost:8080/xxx 看到的是你注册的 OnErrorCode 响应;但上线后经 Nginx 反代,真实 404 可能由 Nginx 直接返回,你的 Iris 404 handler 根本不执行。


















