生产环境必须彻底屏蔽 /swagger 路由,仅注释接口无效;应通过环境判断控制 ginSwagger.WrapHandler 是否注册,并配合构建标签、反向代理拦截及阻断 /docs/swagger.json。

生产环境必须彻底屏蔽 /swagger 路由,仅靠注释隐藏接口或参数完全无效——Swagger UI 页面本身就是一个独立的 Web 服务入口,只要它能被访问,所有已生成的接口(包括被 @ApiIgnore 标记的)都可能被手动构造请求调用。
为什么只注释接口没用?
在 Gin 中,@ApiIgnore 或 @Param hidden 这类注释只影响 swag init 生成的 docs/swagger.json 内容,但不阻止 Swagger UI 页面加载。一旦页面打开,用户仍可:
- 直接查看
/docs/swagger.json获取完整接口列表 - 手动拼接 URL 发起请求(如
POST /api/v1/internal/refresh),绕过文档展示逻辑 - 利用浏览器开发者工具抓包,发现未在文档中显示但实际存在的路由
也就是说:注释控制的是“文档里写不写”,而不是“接口存不存在”或“页面能不能打开”。
Gin 中禁用 Swagger UI 的硬性开关
真正起效的方式是让 gin-swagger 中间件在非开发环境彻底不注册路由。关键不是删注释,而是控制 WrapHandler 是否被挂载:
- 用
os.Getenv("ENV")或viper.GetString("env")判断当前环境 - 只在
dev或test环境调用r.GET("/swagger/*any", ginSwagger.WrapHandler(docs.Handler)) - 生产环境完全跳过这行代码,
/swagger路径根本不会被 Gin 路由表收录
示例片段:
if os.Getenv("ENV") == "dev" {
r.GET("/swagger/*any", ginSwagger.WrapHandler(docs.Handler))
}
注意:docs.Handler 是函数值,ginSwagger.WrapHandler(docs.Handler) 才是合法中间件;写成 ginSwagger.WrapHandler(docs.Handler()) 会 panic。
额外加固:防止静态资源泄露
即使不注册路由,如果 docs 包已被导入(例如 _ "your-module/docs"),其内部的 swagger.json 和 UI 静态文件仍在内存中。为防意外暴露,建议:
- 在构建生产镜像时,用
go build -tags=prod配合构建标签跳过 docs 包导入 - 或在
main.go顶部加条件编译:// +build !prod,让 docs 导入仅在非 prod 下生效 - 反向代理层(如 Nginx)增加拦截规则:
location ^~ /swagger { return 404; },作为兜底
最易忽略的一点:很多团队只关了 UI 页面,却忘了 /docs/swagger.json 本身仍是公开可访问的 JSON 文件——它才是接口元数据的真实来源,必须一并阻断。











