{"record":{"id":"ae0518ec0d7d9a9e","repo":"AlistGo/alist","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":"internal/net/serve.go","lineNumber":272,"sourceCode":"\t\t}\n\t\tall, _ := io.ReadAll(reader)\n\t\t_ = res.Body.Close()\n\t\tmsg := string(all)\n\t\tlog.Debugln(msg)\n\t\treturn res, fmt.Errorf(\"http request [%s] failure,status: %d response:%s\", URL, res.StatusCode, msg)\n\t}\n\treturn res, nil\n}\n\nvar once sync.Once\nvar httpClient *http.Client\n\nfunc HttpClient() *http.Client {\n\tonce.Do(func() {\n\t\thttpClient = NewHttpClient()\n\t\thttpClient.CheckRedirect = func(req *http.Request, via []*http.Request) error {\n\t\t\tif len(via) >= 10 {\n\t\t\t\treturn errors.New(\"stopped after 10 redirects\")\n\t\t\t}\n\t\t\treq.Header.Del(\"Referer\")\n\t\t\treturn nil\n\t\t}\n\t})\n\treturn httpClient\n}\n\nfunc NewHttpClient() *http.Client {\n\treturn &http.Client{\n\t\tTimeout: time.Hour * 48,\n\t\tTransport: &http.Transport{\n\t\t\tProxy:           http.ProxyFromEnvironment,\n\t\t\tTLSClientConfig: &tls.Config{InsecureSkipVerify: conf.Conf.TlsInsecureSkipVerify},\n\t\t},\n\t}\n}\n","sourceCodeStart":254,"sourceCodeEnd":290,"githubUrl":"https://github.com/AlistGo/alist/blob/843d9dc8149126976b2625911e45a4d3ffd6f2f5/internal/net/serve.go#L254-L290","documentation":"'stopped after 10 redirects' is produced by the CheckRedirect policy on AList's shared HTTP client (internal/net/serve.go). Any request made through net.HttpClient() that follows more than 10 redirects is aborted with this error; the policy also strips the Referer header on each hop. It mirrors net/http's own default redirect cap but with an explicit error string.","triggerScenarios":"Downloading or proxying a URL (offline download, remote fetch, any net.HttpClient() usage) whose chain of 3xx responses exceeds 10 hops; redirect loops (A->B->A...) between two URLs; a server that bounces between http/https or issues a session redirect on every request.","commonSituations":"Expired short-link services redirecting through many ad/interstitial pages; misconfigured origins with rewrite loops; offline-download URLs from link shorteners; CDNs repeatedly issuing trailing-slash redirects. Fixing the target URL or the origin's redirect logic is the real remedy.","solutions":["Fetch the URL with curl -L -v and count the hops; eliminate the loop or shorten the chain at the source","Update the stored URL to the final destination after the redirect chain","If the origin legitimately needs more hops, build a custom client with NewHttpClient() and a raised CheckRedirect limit instead of the shared one","Check for cookie/session redirects that make each hop re-redirect, and carry cookies so the chain terminates"],"exampleFix":"// before\nresp, err := net.HttpClient().Get(url)\n\n// after\nclient := net.NewHttpClient()\nclient.CheckRedirect = func(req *http.Request, via []*http.Request) error {\n    if len(via) >= 20 { return errors.New(\"stopped after 20 redirects\") }\n    req.Header.Del(\"Referer\")\n    return nil\n}\nresp, err := client.Get(url)","handlingStrategy":"retry","validationCode":"# pre-resolve the URL outside the app to bound redirect chains\ncurl -sIL -o /dev/null -w '%{num_redirects}' <url>  # keep only if <= 10","typeGuard":"func isRedirectLimit(err error) bool {\n    if ue, ok := err.(*url.Error); ok { err = ue.Err }\n    return err != nil && strings.Contains(err.Error(), \"stopped after 10 redirects\")\n}","tryCatchPattern":"resp, err := net.HttpClient().Get(u)\nif isRedirectLimit(err) {\n    // resolve the final URL out-of-band and retry against it once\n}","preventionTips":["Store final destination URLs, not short-links","Keep a custom client with a higher cap for known-long chains","Watch for redirect loops on origins you control","Strip cookies/sessions that trigger hop amplification"],"tags":["http","redirect","network","download"],"backgroundTag":null,"analyzedSha":"843d9dc8149126976b2625911e45a4d3ffd6f2f5","analyzedAt":"2026-08-15T12:14:11.722Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}