{"record":{"id":"68df2d16457da96a","repo":"golang/go","slug":"redirected-from-secure-url-s-to-insecure-url-s","errorCode":null,"errorMessage":"redirected from secure URL %s to insecure URL %s","messagePattern":"redirected from secure URL (.+?) to insecure URL (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/cmd/go/internal/web/http.go","lineNumber":60,"sourceCode":"\tTransport: &http.Transport{\n\t\tProxy: http.ProxyFromEnvironment,\n\t\tTLSClientConfig: &tls.Config{\n\t\t\tInsecureSkipVerify: true,\n\t\t},\n\t},\n}\n\nvar securityPreservingDefaultClient = securityPreservingHTTPClient(http.DefaultClient)\n\n// securityPreservingHTTPClient returns a client that is like the original\n// but rejects redirects to plain-HTTP URLs if the original URL was secure.\nfunc securityPreservingHTTPClient(original *http.Client) *http.Client {\n\tc := new(http.Client)\n\t*c = *original\n\tc.CheckRedirect = func(req *http.Request, via []*http.Request) error {\n\t\tif len(via) > 0 && via[0].URL.Scheme == \"https\" && req.URL.Scheme != \"https\" {\n\t\t\tlastHop := via[len(via)-1].URL\n\t\t\treturn fmt.Errorf(\"redirected from secure URL %s to insecure URL %s\", lastHop, req.URL)\n\t\t}\n\t\treturn checkRedirect(req, via)\n\t}\n\treturn c\n}\n\nfunc checkRedirect(req *http.Request, via []*http.Request) error {\n\t// Go's http.DefaultClient allows 10 redirects before returning an error.\n\t// Mimic that behavior here.\n\tif len(via) >= 10 {\n\t\treturn errors.New(\"stopped after 10 redirects\")\n\t}\n\thasGoGet1 := via[len(via)-1].URL.Query().Get(\"go-get\") == \"1\"\n\tif hasGoGet1 {\n\t\tif len(req.URL.RawQuery) > 0 {\n\t\t\treq.URL.RawQuery += \"&\"\n\t\t}\n\t\treq.URL.RawQuery += \"go-get=1\"","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/cmd/go/internal/web/http.go#L42-L78","documentation":"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.","triggerScenarios":"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'.","commonSituations":"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.","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"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Detect an HTTPS to HTTP downgrade before relying on the fetch\nfunc noDowngrade(start string) error {\n  c := &http.Client{}\n  c.CheckRedirect = func(req *http.Request, via []*http.Request) error {\n    if len(via) > 0 && via[0].URL.Scheme == \"https\" && req.URL.Scheme != \"https\" {\n      return fmt.Errorf(\"downgrade to %s\", req.URL)\n    }\n    if len(via) >= 10 { return errors.New(\"too many redirects\") }\n    return nil\n  }\n  _, err := c.Get(start)\n  return err\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Serve HTTPS end-to-end; never redirect HTTPS to HTTP","Set GOINSECURE only for specific private hosts that genuinely need HTTP"],"tags":["go","web","security","network","tls"],"backgroundTag":null,"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}