amir20/dozzle · error

registry omitted Docker-Content-Digest for

Error message

registry omitted Docker-Content-Digest for %s

What it means

After a successful manifest request, Digest reads the Docker-Content-Digest response header. Registries are expected to return it; if it is missing or empty, the client cannot compare remote vs local digests and returns this error.

Solutions

  1. Bypass or reconfigure the proxy to pass through the Docker-Content-Digest header
  2. Test directly: curl -I https://<registry>/v2/<repo>/manifests/<tag> and check the header
  3. Use a compliant registry (Docker Hub, GHCR, ECR, Harbor) or upgrade the custom registry
  4. As a fallback, fetch the manifest body and compute the sha256 digest locally

Example fix

// before
digest := resp.Header.Get("Docker-Content-Digest")
// after (caller-side fallback)
digest := resp.Header.Get("Docker-Content-Digest")
if digest == "" {
    b, _ := io.ReadAll(resp.Body)
    sum := sha256.Sum256(b)
    digest = "sha256:" + hex.EncodeToString(sum[:])
}
Defensive patterns

Strategy: fallback

Validate before calling

resp, err := http.Head(manifestURL)
if err == nil && resp.Header.Get("Docker-Content-Digest") == "" {
    return fmt.Errorf("registry does not expose Docker-Content-Digest; plan fallback")
}

Try / catch

digest, err := client.Digest(ref)
if err != nil && strings.Contains(err.Error(), "omitted Docker-Content-Digest") {
    digest, err = computeDigestFromManifestBody(ref) // sha256 the manifest bytes
}

Prevention

When it happens

Trigger: The manifest HEAD/GET returns a success status but the Docker-Content-Digest header is absent, which happens with proxies that strip headers, misconfigured reverse proxies, or non-OCI-compliant registries.

Common situations: Corporate proxies or CDNs removing response headers, custom registries that don't implement the Docker Registry v2 header contract, or a caching layer serving stale headerless responses.

Related errors


AI-assisted analysis of amir20/dozzle@d9463cbe21 (2026-09-07). Data as JSON: /api/errors/31d4707e2591bb1c. Report an issue: GitHub.

Appendix: source

Thrown at internal/imagecheck/registry.go:116

		Int("status", resp.StatusCode).
		Str("contentType", resp.Header.Get("Content-Type")).
		Msg("image update check: manifest response")

	switch resp.StatusCode {
	case http.StatusOK:
	case http.StatusUnauthorized, http.StatusForbidden:
		return "", ErrAuthRequired
	case http.StatusNotFound:
		return "", ErrNotFound
	case http.StatusTooManyRequests:
		return "", ErrRateLimited
	default:
		return "", fmt.Errorf("registry returned %s for %s", resp.Status, ref.Repository)
	}

	digest := resp.Header.Get("Docker-Content-Digest")
	if digest == "" {
		return "", fmt.Errorf("registry omitted Docker-Content-Digest for %s", ref.Repository)
	}

	log.Debug().Str("repository", ref.Repository).Str("digest", digest).Msg("image update check: registry digest")

	return digest, nil
}

func (r *Registry) head(ctx context.Context, ref Reference, token string) (*http.Response, error) {
	req, err := http.NewRequestWithContext(ctx, http.MethodHead, ref.manifestURL(), nil)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Accept", acceptManifests)
	if token != "" {
		req.Header.Set("Authorization", "Bearer "+token)
	}

	return r.client.Do(req)

View on GitHub (pinned to d9463cbe21)