echo默认不自动处理head请求,因为它严格按注册的http方法匹配路由;若只注册e.get("/users", h),则head /users会404,必须显式注册e.head或启用v5.3.0的autohandlehead配置。

为什么Echo默认不自动处理HEAD请求
Echo不会把GET路由自动映射到HEAD——它严格按注册的method匹配。如果你只写了e.GET("/users", handler),那么HEAD /users会直接404,哪怕逻辑上HEAD只需返回头、不用响应体。
常见错误现象是前端发HEAD探活或CDN预检失败,日志里只有code=404,但你根本没意识到该加路由。
- 必须显式注册:
e.HEAD("/users", handler),且handler里别读c.Request().Body(HEAD本就不带body) - 若想复用GET逻辑,handler里统一用
c.Response().Writer写header,不调c.String()等写body的方法 - 别在中间件里盲目
if r.Method == "HEAD" { return nil }——这会跳过鉴权、日志等关键逻辑
OPTIONS预检请求必须手动拦截并立即返回
Echo不内置CORS中间件,也不自动响应OPTIONS。如果前端带Authorization或自定义header发请求,浏览器会先发OPTIONS,而Echo默认把它当普通请求往下走,直到你的业务handler试图读r.Body才panic。
Echo框架 5.1.0 版本源码包下载,适合关注 RealIP 行为变化、StartConfig.Listener、NewDefaultFS 和观测性中间件入口的开发团队。
典型错误是漏掉return,导致OPTIONS分支执行完后继续跑后续逻辑:
if c.Request().Method == "OPTIONS" {
c.Response().Header().Set("Access-Control-Allow-Origin", "https://myapp.com")
c.Response().Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
c.Response().Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
c.NoContent(http.StatusOK) // 必须有这一行,且不能少
return // 这个return绝对不能删!
}
// 后续才是GET/POST逻辑
-
Access-Control-Allow-Origin设为*时,不能同时允许Credentials;若前端用了withCredentials: true,这里必须填具体域名 - 所有
Access-Control-头必须在OPTIONS响应中一次性写全,不能靠中间件分段写(部分中间件可能覆盖前序设置) - 别依赖第三方CORS包自动处理——很多包对
OPTIONS只做透传,没做return兜底,线上容易出静默500
如何让HEAD和OPTIONS共享同一套响应头逻辑
重复写两遍Header().Set既易错又难维护。更稳妥的方式是抽离一个函数统一设置CORS和通用头:
func setCommonHeaders(c echo.Context) {
c.Response().Header().Set("Access-Control-Allow-Origin", "https://myapp.com")
c.Response().Header().Set("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, DELETE, OPTIONS")
c.Response().Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Request-ID")
c.Response().Header().Set("X-Content-Type-Options", "nosniff")
}
e.HEAD("/api/users", func(c echo.Context) error {
setCommonHeaders(c)
return c.NoContent(http.StatusOK)
})
e.OPTIONS("/api/users", func(c echo.Context) error {
setCommonHeaders(c)
return c.NoContent(http.StatusOK)
})
- 注意
Access-Control-Allow-Methods要把HEAD和OPTIONS都列进去,否则浏览器预检会拒绝后续请求 - 不要在
setCommonHeaders里写Content-Length或ETag——HEAD需要这些,OPTIONS不需要,混用会导致响应异常 - 如果路由层级深(如
/v1/users/:id),优先在全局OPTIONS中间件里处理通配,而不是每个路由单独注册
HEAD请求体为空时,别误判为客户端错误
Go标准库对HEAD请求的Content-Length头处理很松:即使值非零,http.Request.Body也永远是http.NoBody。但有些代理或测试工具会乱设这个头,导致你的handler里c.Request().ContentLength > 0为真,进而触发不必要的校验逻辑。
- 判断是否为HEAD时,只看
c.Request().Method == "HEAD",不要结合ContentLength或Body状态做分支 - 若handler需兼容GET/HEAD,所有涉及
io.Copy、json.NewDecoder等读body的操作,必须包裹在if c.Request().Method != "HEAD"里 - 日志中间件里对HEAD请求打点时,跳过
body size字段输出,避免日志里出现body_size=-1这类误导信息
Content-Length对HEAD有效),哪些只是业务约定(如X-RateLimit-Remaining)。一不留神,CDN缓存或浏览器预检就会出问题。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










