我在Go中编写自己的ReverseProxy。ReverseProxy应该连接我的go-webserver和apache2 webserver。但是,当我在另一个IP地址上运行反向代理时,然后在我的Apache2 Web服务器上,当反向代理将请求发送到apache时,我的apache-logfile中出现以下错误。
"Hosname xxxx provided via sni and hostname xxxx2 provided via http are different"
我的反向代理和apache-webserver在https上运行。
这里有一些代码:
func (p *Proxy) directorApache(req *http.Request) { mainServer := fmt.Sprintf("%s:%d", Config.HostMain, Config.PortMain) req.URL.Scheme = "https" req.URL.Host = mainServer } func (p *Proxy) directorGo(req *http.Request) { goServer := fmt.Sprintf("%s:%d", Config.GoHost, Config.GoPort) req.URL.Scheme = "http" req.URL.Host = goServer } func (p *Proxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) { fmt.Println(req.URL.Path) if p.isGoRequest(req) { fmt.Println("GO") p.goProxy.ServeHTTP(rw, req) return } p.httpProxy.ServeHTTP(rw, req) } func main() { var configPath = flag.String("conf", "./configReverse.json", "Path to the Json config file.") flag.Parse() proxy := New(*configPath) cert, err := tls.LoadX509KeyPair(Config.PathCert, Config.PathPrivateKey) if err != nil { log.Fatalf("server: loadkeys: %s", err) } config := tls.Config{InsecureSkipVerify: true, Certificates: []tls.Certificate{cert}} listener, err := net.Listen("tcp", net.JoinHostPort(proxy.Host, strconv.Itoa(proxy.Port))) if err != nil { log.Fatalf("server: listen: %s", err) } log.Printf("server: listening on %s") proxy.listener = tls.NewListener(listener, &config) serverHTTPS := &http.Server{ Handler: proxy.mux, TLSConfig: &config, } if err := serverHTTPS.Serve(proxy.listener); err != nil { log.Fatal("SERVER ERROR:", err) } }
也许有人对此有想法。
假设您正在向发起HTTP请求https://your- proxy.local。您的请求处理程序采用该http.Request结构,并将其URL字段重写为https://your-apache- backend.local。
https://your- proxy.local
http.Request
URL
https://your-apache- backend.local
您没有考虑的是,原始HTTP请求还包含一个Host标头(Host: your- proxy.local)。将相同的请求传递给该请求时http://your-apache- backend.local,该请求中的Host标头仍会显示Host: your-proxy.local。这就是Apache所抱怨的。
Host
Host: your- proxy.local
http://your-apache- backend.local
Host: your-proxy.local
当您将TLS与服务器名称指示(SNI)一起使用时,请求主机名不仅将用于DNS解析,还将选择用于建立TLS连接的SSL证书。Host另一方面,HTTP 1.1 标头用于通过Apache区分多个虚拟主机。两个名称 必须匹配 。Apache HTTPD Wiki中也提到了此问题:
SNI /请求主机名不匹配,或者SNI提供了主机名而请求没有。 这是一个浏览器错误。Apache将拒绝该请求,并显示400类型错误。
SNI /请求主机名不匹配,或者SNI提供了主机名而请求没有。
这是一个浏览器错误。Apache将拒绝该请求,并显示400类型错误。
还要重写Host标题。如果要保留原始Host标头,可以将其存储在X-Forwarded- Host标头中(这是非标准标头,但在反向代理中广泛使用):
X-Forwarded- Host
func (p *Proxy) directorApache(req *http.Request) { mainServer := fmt.Sprintf("%s:%d", Config.HostMain, Config.PortMain) req.URL.Scheme = "https" req.URL.Host = mainServer req.Header.Set("X-Forwarded-Host", req.Header().Get("Host")) req.Host = mainServer }