Nginx必须先做二级域名反向代理,Gin不处理域名路由;多域名分流应在Nginx层通过server_name实现,Gin仅专注路径与方法处理,并可通过中间件基于Host动态适配行为。

Gin 本身不处理域名路由,它只管 HTTP 请求的路径(Path)和方法(Method)。多域名(如 api.example.com、admin.example.com)的分流必须由前端反向代理(如 Nginx)完成,Gin 应用只需专注自身逻辑——但你得让 Gin 正确响应跨域、识别来源、适配不同域名下的行为。
为什么不能在 Gin 里用 server_name 或 Host 做路由分支
因为 Gin 是应用层框架,它收到的请求已经过 Nginx(或其它网关)转发,r.Request.Host 虽然能读到原始 Host,但:
- 靠
if r.Request.Host == "api.example.com"手动分发路由,会污染业务逻辑,难以维护 - 无法解决静态资源路径冲突(比如两个域名都访问
/favicon.ico,但期望返回不同文件) - 无法统一管理 SSL、HTTP/2、缓存头等必须在代理层控制的能力
- 一旦加了 CDN 或云 WAF,
Host可能被改写或透传不一致
Nginx 必须先做二级域名反向代理
这是不可绕过的前提。Gin 应用只需监听本地端口(如 :3000),所有域名流量由 Nginx 按 server_name 分流到对应后端。
例如,为 api.example.com 和 admin.example.com 各建一个 server 块:
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
server {
listen 80;
server_name admin.example.com;
location / {
proxy_pass http://127.0.0.1:3001;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
关键点:
-
proxy_set_header Host $host必须保留,否则 Gin 的c.Request.Host会变成127.0.0.1:3000 - 两个 Gin 实例可共用同一份代码,启动时用不同端口 + 环境变量区分行为(如读取
APP_ENV=api) - 若想单个 Gin 实例支撑多个域名,需在中间件里检查
c.Request.Host并设置上下文字段(见下一条)
如何让单个 Gin 实例感知当前域名并差异化响应
适用于轻量场景:比如同一套 API 服务,但 admin.example.com 需要额外鉴权头、web.example.com 允许 CORS、static.example.com 返回静态文件。
开箱即用的技能链路由引擎。13 条预定义链覆盖搜索、开发、审查、MLOps、法律、创意等场景,三层路由架构(触发词→SAD反馈→DAG编排),recall@10=96.97%。配置驱动(chains.yaml),零代码扩展。pip install skill-weave-chains 一键安装。
做法是在最外层加一个中间件:
func domainMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
host := c.Request.Host
switch host {
case "api.example.com":
c.Set("domain_role", "api")
c.Set("allowed_origins", []string{"https://web.example.com"})
case "admin.example.com":
c.Set("domain_role", "admin")
c.Set("allowed_origins", []string{"https://admin.example.com"})
default:
c.AbortWithStatus(http.StatusForbidden)
return
}
c.Next()
}
}
后续 handler 中可用 c.GetString("domain_role") 判断;CORS 配置也应基于此动态生成,而非硬编码 AllowedOrigins。
注意:
- 别在
gin-contrib/cors初始化时直接传死数组,要用cors.Config{AllowOriginFunc: ...}动态回调 - 如果启用了
AllowCredentials: true,AllowOriginFunc返回的 origin 必须和请求头Origin完全一致(协议+域名+端口),不能是子域名通配 - 务必在
domainMiddleware之后挂载 CORS 中间件,否则c.Request.Host还没被检查
常见踩坑:CORS 报错但控制台无提示,请求卡在 pending
这几乎 100% 是 Access-Control-Allow-Credentials: "true" 和 Access-Control-Allow-Origin: "*" 同时出现导致的。浏览器静默丢弃响应,不报 CORS error,也不触发 onerror。
排查步骤:
- 用 curl 模拟带
Origin和Cookie的请求:curl -H "Origin: https://web.example.com" -b "session=xxx" http://localhost:3000/api/user - 检查响应头是否同时存在
Access-Control-Allow-Origin: *和Access-Control-Allow-Credentials: true - 确认
gin-contrib/cors版本 ≥ 1.5.0,且配置中没漏掉Vary: Origin(它默认加了) - 如果用了自定义中间件,确认没重复写
SetHeader("Access-Control-Allow-Origin", ...)导致 header 冲突
真正难调试的点在于:问题不在 Gin 代码里,而在 Nginx 是否透传了 Origin 头、是否缓存了错误的 CORS 响应、以及前端发请求时是否漏了 credentials: 'include' ——三者缺一不可。


















