diff --git a/weed/s3api/auth_signature_v4.go b/weed/s3api/auth_signature_v4.go index d0297d623..c199e2ea2 100644 --- a/weed/s3api/auth_signature_v4.go +++ b/weed/s3api/auth_signature_v4.go @@ -591,44 +591,60 @@ func extractSignedHeaders(signedHeaders []string, r *http.Request) (http.Header, // extractHostHeader returns the value of host header if available. func extractHostHeader(r *http.Request) string { - // Check for X-Forwarded-Host header first, which is set by reverse proxies - if forwardedHost := r.Header.Get("X-Forwarded-Host"); forwardedHost != "" { - // Check if X-Forwarded-Host already contains a port - // This handles proxies (like Traefik, HAProxy) that include port in X-Forwarded-Host - if _, _, err := net.SplitHostPort(forwardedHost); err == nil { - // X-Forwarded-Host already contains a port (e.g., "example.com:8443" or "[::1]:8080") - // Use it as-is - return forwardedHost + forwardedHost := r.Header.Get("X-Forwarded-Host") + forwardedPort := r.Header.Get("X-Forwarded-Port") + forwardedProto := r.Header.Get("X-Forwarded-Proto") + + // If X-Forwarded-Host is set, use that as the host. + // If X-Forwarded-Port is set, use that too to form the host. + // If X-Forwarded-Proto is set, check if is it default to omit the port. + if forwardedHost != "" { + extractedHost := forwardedHost + host, port, err := net.SplitHostPort(extractedHost) + if err == nil { + extractedHost = host + if forwardedPort == "" { + forwardedPort = port + } } - - // An IPv6 address literal must be enclosed in square brackets. - if ip := net.ParseIP(forwardedHost); ip != nil && strings.Contains(forwardedHost, ":") { - forwardedHost = "[" + forwardedHost + "]" + scheme := r.URL.Scheme + if forwardedProto != "" { + scheme = forwardedProto } - - // X-Forwarded-Host doesn't contain a port, check if X-Forwarded-Port is provided - if forwardedPort := r.Header.Get("X-Forwarded-Port"); forwardedPort != "" { - // Determine the protocol to check for standard ports - proto := strings.ToLower(r.Header.Get("X-Forwarded-Proto")) - // Only add port if it's not the standard port for the protocol - if (proto == "https" && forwardedPort != "443") || (proto != "https" && forwardedPort != "80") { - return forwardedHost + ":" + forwardedPort - } + if !isDefaultPort(scheme, forwardedPort) { + extractedHost = net.JoinHostPort(extractedHost, forwardedPort) + } + return extractedHost + } else { + // Go http server removes "host" from Request.Header + host := r.Host + if host == "" { + host = r.URL.Host } - // Using reverse proxy with X-Forwarded-Host (standard port or no port forwarded). - return forwardedHost + h, port, err := net.SplitHostPort(host) + if err != nil { + return host + } + if isDefaultPort(r.URL.Scheme, port) { + return h + } + return host } +} - hostHeaderValue := r.Host - // For standard requests, this should be fine. - if r.Host != "" { - return hostHeaderValue +func isDefaultPort(scheme, port string) bool { + if port == "" { + return true } - // If no host header is found, then check for host URL value. - if r.URL.Host != "" { - hostHeaderValue = r.URL.Host + + switch port { + case "80": + return strings.EqualFold(scheme, "http") + case "443": + return strings.EqualFold(scheme, "https") + default: + return false } - return hostHeaderValue } // getScope generate a string of a specific date, an AWS region, and a service.