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

  1. Fetch the final URL directly — resolve the chain with `curl -IL <url>` and use the last location
  2. Fix the redirect loop on the server/CDN configuration if you control it
  3. 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

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


AI-assisted analysis of gohugoio/hugo@8a468df065 (2026-07-31). Data as JSON: /data/errors/adc999e39a9d1e47.json. Report an issue: GitHub ↗.