{"record":{"id":"e4bf78e5237e2903","repo":"golang/go","slug":"stopped-after-10-redirects","errorCode":null,"errorMessage":"stopped after 10 redirects","messagePattern":"stopped after 10 redirects","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/cmd/go/internal/web/http.go","lineNumber":71,"sourceCode":"// 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\"\n\t}\n\n\tintercept.Request(req)\n\treturn nil\n}\n\nfunc get(security SecurityMode, url *urlpkg.URL) (*Response, error) {\n\tstart := time.Now()\n\n\tif url.Scheme == \"file\" {\n\t\treturn getFile(url)","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/cmd/go/internal/web/http.go#L53-L89","documentation":"checkRedirect is the http.Client.CheckRedirect used for go-get discovery fetches. It mimics net/http's limit of 10 redirects and returns this error once the hop count reaches 10, preventing redirect loops.","triggerScenarios":"Fetching a vanity-import page (or module URL) whose redirect chain exceeds 10 hops.","commonSituations":"Misconfigured vanity servers with redirect loops; aggressive CDN/load-balancer redirects; HTTP->HTTPS cascades.","solutions":["Fix the vanity server's redirect chain to stay under 10 hops.","Bypass vanity discovery: use the direct VCS URL (e.g. go get github.com/user/repo).","Audit the server for accidental redirect loops."],"exampleFix":"# before\n$ go get example.com/lib   # vanity server loops >10 redirects\n\n# after\n$ go get github.com/user/lib   # direct VCS path","handlingStrategy":"validation","validationCode":"// Cap redirect handling when fetching vanity URLs yourself.\nfunc limitedRedirectClient() *http.Client {\n    c := *http.DefaultClient\n    c.CheckRedirect = func(req *http.Request, via []*http.Request) error {\n        if len(via) >= 10 { return errors.New(\"too many redirects\") }\n        return nil\n    }\n    return &c\n}","typeGuard":"null","tryCatchPattern":"null","preventionTips":["Prefer direct VCS URLs (github.com/...) over vanity paths to skip discovery redirects.","Audit vanity servers for redirect loops after deploys.","Use GOPROXY for module downloads to avoid vanity HTTP chains."],"tags":["network","http","redirects","vanity-import"],"backgroundTag":null,"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T12:31:55.035Z"}