gohugoio/hugo · error
stopped after 10 redirects
Error message
stopped after 10 redirects
What it means
Returned by the CheckRedirect hook of the HTTP client Hugo uses for remote resources (resources.GetRemote and remote data fetches). After 10 redirect hops the client aborts, mirroring Go's default policy, to prevent infinite redirect loops when fetching remote content.
Source
Thrown at resources/resource_factories/create/create.go:106
httpTimeout := 2 * time.Minute // Need to cover retries.
if httpTimeout < (rs.Cfg.Timeout() + 30*time.Second) {
httpTimeout = rs.Cfg.Timeout() + 30*time.Second
}
return &Client{
rs: rs,
httpCacheConfig: httpCacheConfig,
resourceIDDispatcher: resourceIDDispatcher,
remoteResourceChecker: remoteResourceChecker,
remoteResourceLogger: rs.Logger.InfoCommand("remote"),
httpClient: &http.Client{
Timeout: httpTimeout,
CheckRedirect: func(req *http.Request, via []*http.Request) error {
if err := rs.ExecHelper.Sec().CheckAllowedHTTPURL(req.URL.String()); err != nil {
return err
}
if len(via) >= 10 {
return errors.New("stopped after 10 redirects")
}
return nil
},
Transport: &httpcache.Transport{
Cache: fileCache.AsHTTPCache(),
CacheKey: func(req *http.Request) string {
return resourceIDDispatcher.Get(req.Context())
},
Around: func(req *http.Request, key string) func() {
return fileCache.NamedLock(key)
},
AlwaysUseCachedResponse: func(req *http.Request, key string) bool {
return !httpCacheConfig.For(req.URL.String())
},
ShouldCache: func(req *http.Request, resp *http.Response, key string) bool {
return shouldCache(resp.StatusCode)
},
CanStore: func(reqCacheControl, respCacheControl httpcache.CacheControl) (canStore bool) {View on GitHub ↗ (pinned to 8a468df065)
Solutions
- Fetch the final URL directly — resolve the chain with `curl -IL <url>` and use the last location
- Fix the redirect loop on the server/CDN configuration if you control it
- Use https:// and the canonical host (with/without www) to skip unnecessary hops
Example fix
// before
{{ $r := resources.GetRemote "http://short.link/abc" }}
// after (use the resolved final URL)
{{ $r := resources.GetRemote "https://cdn.example.com/data.json" }} Defensive patterns
Strategy: retry
Validate before calling
{{ $url := "https://example.com/api" }}{{/* prefer final URLs, not shortener/redirect chains */}} Try / catch
{{ with try (resources.GetRemote $url) }}{{ with .Err }}{{ warnf "remote fetch failed: %s" . }}{{ else }}{{ with .Value }}...{{ end }}{{ end }}{{ end }} Prevention
- Use the final resolved URL instead of URLs behind long redirect chains (curl -sIL to inspect the chain first)
- Check .Err on resources.GetRemote results rather than assuming success
- Beware redirect loops caused by missing trailing slashes or http→https→http bounces
- Cache remote resources (cacheDir / getresource cache config) so transient redirect issues don't break every build
When it happens
Trigger: `resources.GetRemote "https://..."` (or other remote resource fetches) where the server responds with a chain of 10+ 3xx redirects — typically a redirect loop (A→B→A) or a long http→https→www→CDN chain.
Common situations: URLs behind auth walls that bounce between login pages, misconfigured CDN/hosting redirect rules, fetching an http:// URL that ping-pongs with https, or shortened/tracking URLs that chain many hops.
Related errors
- retry timeout (configured to %s) fetching remote resource: %
- redirects must have either From or FromRe set
- failed to compile Redirect glob %q: %w
- failed to compile Redirect regexp %q: %w
- failed to compile Redirect header glob %q: %w
AI-assisted analysis of gohugoio/hugo@8a468df065 (2026-07-31).
Data as JSON: /data/errors/adc999e39a9d1e47.json.
Report an issue: GitHub ↗.