golang/go · error
redirected from secure URL %s to insecure URL %s
Error message
redirected from secure URL %s to insecure URL %s
What it means
The security-preserving HTTP client (used for all go command fetches) blocks a redirect that downgrades from HTTPS to HTTP. Names the last secure hop and the insecure target. Prevents MITM downgrade attacks during module/vanity fetch.
Source
Thrown at src/cmd/go/internal/web/http.go:60
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
},
}
var securityPreservingDefaultClient = securityPreservingHTTPClient(http.DefaultClient)
// securityPreservingHTTPClient returns a client that is like the original
// but rejects redirects to plain-HTTP URLs if the original URL was secure.
func securityPreservingHTTPClient(original *http.Client) *http.Client {
c := new(http.Client)
*c = *original
c.CheckRedirect = func(req *http.Request, via []*http.Request) error {
if len(via) > 0 && via[0].URL.Scheme == "https" && req.URL.Scheme != "https" {
lastHop := via[len(via)-1].URL
return fmt.Errorf("redirected from secure URL %s to insecure URL %s", lastHop, req.URL)
}
return checkRedirect(req, via)
}
return c
}
func checkRedirect(req *http.Request, via []*http.Request) error {
// Go's http.DefaultClient allows 10 redirects before returning an error.
// Mimic that behavior here.
if len(via) >= 10 {
return errors.New("stopped after 10 redirects")
}
hasGoGet1 := via[len(via)-1].URL.Query().Get("go-get") == "1"
if hasGoGet1 {
if len(req.URL.RawQuery) > 0 {
req.URL.RawQuery += "&"
}
req.URL.RawQuery += "go-get=1"View on GitHub (pinned to b6b368adc5)
Solutions
- Fix the server to keep HTTPS end-to-end (correct redirect target)
- If HTTP is genuinely acceptable for a private host, set GOINSECURE=<host> (understanding the downgrade risk)
- Check for a captive portal or corporate proxy injecting the HTTPS to HTTP redirect
Defensive patterns
Strategy: validation
Validate before calling
// Detect an HTTPS to HTTP downgrade before relying on the fetch
func noDowngrade(start string) error {
c := &http.Client{}
c.CheckRedirect = func(req *http.Request, via []*http.Request) error {
if len(via) > 0 && via[0].URL.Scheme == "https" && req.URL.Scheme != "https" {
return fmt.Errorf("downgrade to %s", req.URL)
}
if len(via) >= 10 { return errors.New("too many redirects") }
return nil
}
_, err := c.Get(start)
return err
} Prevention
- Serve HTTPS end-to-end; never redirect HTTPS to HTTP
- Set GOINSECURE only for specific private hosts that genuinely need HTTP
When it happens
Trigger: An https:// URL responds 301/302/307/308 with a Location: http://... header; via[0].URL.Scheme is 'https' and req.URL.Scheme is not 'https'.
Common situations: Server misconfigured to redirect HTTPS to HTTP; captive portal or MITM proxy injecting the redirect; host that legitimately serves only HTTP but GOINSECURE isn't set.
Related errors
- tls: invalid server key share
- tls: invalid signature by the server certificate: {err}
- tls: invalid server finished hash
- server's certificate is not allowed in FIPS 140-3 mode
- tls: server sent non-zero legacy TLS compression method
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/68df2d16457da96a.
Report an issue: GitHub.