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

  1. Fix the server to keep HTTPS end-to-end (correct redirect target)
  2. If HTTP is genuinely acceptable for a private host, set GOINSECURE=<host> (understanding the downgrade risk)
  3. 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

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


AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12). Data as JSON: /api/errors/68df2d16457da96a. Report an issue: GitHub.