如何在Golang中实现多域名路由_支持虚拟主机访问

Go中实现多域名路由需自定义Handler解析Host头并分发请求,可为各域名配置独立ServeMux或gorilla/mux路由器,生产环境推荐Nginx/Caddy前置代理。

在 Go 中实现多域名路由(即基于 Host 头的虚拟主机路由),核心是利用 http.ServeMux 的局限性之外的方案——因为标准 http.ServeMux 只匹配路径,不处理 Host。你需要手动解析请求的 Host 字段,并分发到不同处理器。以下是清晰、实用的实现方式。

使用自定义 Handler 实现 Host 分发

最直接的方式是写一个顶层 http.Handler,根据 r.Host(或 r.URL.Host)选择对应子路由。注意:Host 值不含端口(如 example.com),若含端口需用 strings.Split(r.Host, ":")[0] 提取。

  • 为每个域名创建独立的 http.ServeMux 或第三方路由器(如 gorilla/muxchi
  • 在主 handler 中检查 r.Host,匹配后调用对应 mux 的 ServeHTTP
  • 未匹配域名可返回 404 或重定向到默认站点

示例:两个域名分别托管不同服务

以下代码启动一个服务器,让 api.example.com 走 API 路由,www.example.com 走 Web 页面路由:

package main

import (
    "fmt"
    "net/http"
)

func main() {
    apiMux := http.NewServeMux()
    apiMux.HandleFunc("/users", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprint(w, "API: users list")
    })

    webMux := http.NewServeMux()
    webMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprint(w, "Web homepage")
    })

    http.ListenAndServe(":8080", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        host := r.Host
        if i := strings.Index(host, ":"); i != -1 {
            host = host[:i] // 去掉端口
        }

        switch host {
        case "api.example.com":
            apiMux.ServeHTTP(w, r)
        case "www.example.com":
            webMux.ServeHTTP(w, r)
        default:
            http.Error(w, "Unknown host", http.StatusNotFound)
        }
    }))
}

结合 gorilla/mux 支持更复杂路由规则

如果各域名下需要路径参数、中间件、子路由等高级功能,推荐用 gorilla/mux 为每个域名单独建 router:

  • 每个域名对应一个 mux.Router,可独立挂载中间件(如日志、CORS)
  • 主 handler 不做业务逻辑,只做 Host 路由,保持职责单一
  • 支持 TLS 时,确保反向代理(如 Nginx)正确透传 Host 头;若直连,客户端 Host 必须准确

生产环境建议:前置反向代理更可靠

纯 Go 实现 Host 路由可行,但生产中更推荐用 Nginx / Caddy 做虚拟主机分发:

  • Nginx 根据 server_name 将请求代理到不同 Go 后端端口(如 api:8081web:8082
  • Go 服务专注业务,无需处理 Host、HTTPS 终止、负载均衡等
  • 便于灰度发布、证书管理、访问日志分离