From 902ee0209635c3524ce76a2c240b56c909c87d41 Mon Sep 17 00:00:00 2001
From: Michael Kriese <michael.kriese@visualon.de>
Date: Tue, 31 Aug 2021 10:23:22 +0200
Subject: [PATCH] feat(manager:docker): use head requests (#11484)

---
 .../docker/__snapshots__/index.spec.ts.snap   | 1063 +----------------
 lib/datasource/docker/common.ts               |   12 +-
 lib/datasource/docker/index.spec.ts           |  136 +--
 lib/datasource/docker/index.ts                |   24 +-
 lib/util/http/index.ts                        |   14 +-
 5 files changed, 107 insertions(+), 1142 deletions(-)

diff --git a/lib/datasource/docker/__snapshots__/index.spec.ts.snap b/lib/datasource/docker/__snapshots__/index.spec.ts.snap
index 031abb8ee9..8cdb641738 100644
--- a/lib/datasource/docker/__snapshots__/index.spec.ts.snap
+++ b/lib/datasource/docker/__snapshots__/index.spec.ts.snap
@@ -1,690 +1,22 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`datasource/docker/index getDigest continues without token if ECR authentication could not be extracted 1`] = `
-Array [
-  Object {
-    "headers": Object {
-      "accept-encoding": "gzip, deflate, br",
-      "host": "123456789.dkr.ecr.us-east-1.amazonaws.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://123456789.dkr.ecr.us-east-1.amazonaws.com/v2/",
-  },
-]
-`;
-
-exports[`datasource/docker/index getDigest continues without token if ECR authentication fails 1`] = `
-Array [
-  Object {
-    "headers": Object {
-      "accept-encoding": "gzip, deflate, br",
-      "host": "123456789.dkr.ecr.us-east-1.amazonaws.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://123456789.dkr.ecr.us-east-1.amazonaws.com/v2/",
-  },
-]
-`;
-
-exports[`datasource/docker/index getDigest continues without token, when no header is present 1`] = `
-Array [
-  Object {
-    "headers": Object {
-      "accept-encoding": "gzip, deflate, br",
-      "host": "index.docker.io",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://index.docker.io/v2/",
-  },
-  Object {
-    "headers": Object {
-      "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json",
-      "accept-encoding": "gzip, deflate, br",
-      "host": "index.docker.io",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://index.docker.io/v2/library/some-dep/manifests/some-new-value",
-  },
-]
-`;
-
-exports[`datasource/docker/index getDigest falls back to body for digest 1`] = `
-Array [
-  Object {
-    "headers": Object {
-      "accept-encoding": "gzip, deflate, br",
-      "host": "index.docker.io",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://index.docker.io/v2/",
-  },
-  Object {
-    "headers": Object {
-      "accept": "application/json",
-      "accept-encoding": "gzip, deflate, br",
-      "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk",
-      "host": "auth.docker.io",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://auth.docker.io/token?service=registry.docker.io&scope=repository:library/some-dep:pull",
-  },
-  Object {
-    "headers": Object {
-      "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json",
-      "accept-encoding": "gzip, deflate, br",
-      "authorization": "Bearer some-token",
-      "host": "index.docker.io",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://index.docker.io/v2/library/some-dep/manifests/some-new-value",
-  },
-]
-`;
-
-exports[`datasource/docker/index getDigest passes credentials to ECR client 1`] = `
-Array [
-  Object {
-    "headers": Object {
-      "accept-encoding": "gzip, deflate, br",
-      "host": "123456789.dkr.ecr.us-east-1.amazonaws.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://123456789.dkr.ecr.us-east-1.amazonaws.com/v2/",
-  },
-  Object {
-    "headers": Object {
-      "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json",
-      "accept-encoding": "gzip, deflate, br",
-      "authorization": "Basic abcdef",
-      "host": "123456789.dkr.ecr.us-east-1.amazonaws.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://123456789.dkr.ecr.us-east-1.amazonaws.com/v2/node/manifests/some-tag",
-  },
-]
-`;
-
-exports[`datasource/docker/index getDigest returns digest 1`] = `
-Array [
-  Object {
-    "headers": Object {
-      "accept-encoding": "gzip, deflate, br",
-      "host": "index.docker.io",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://index.docker.io/v2/",
-  },
-  Object {
-    "headers": Object {
-      "accept": "application/json",
-      "accept-encoding": "gzip, deflate, br",
-      "host": "auth.docker.io",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://auth.docker.io/token?service=registry.docker.io&scope=repository:library/some-dep:pull",
-  },
-  Object {
-    "headers": Object {
-      "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json",
-      "accept-encoding": "gzip, deflate, br",
-      "authorization": "Bearer some-token",
-      "host": "index.docker.io",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://index.docker.io/v2/library/some-dep/manifests/latest",
-  },
-]
-`;
-
-exports[`datasource/docker/index getDigest returns null for 403 with basic authentication 1`] = `
-Array [
-  Object {
-    "headers": Object {
-      "accept-encoding": "gzip, deflate, br",
-      "host": "index.docker.io",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://index.docker.io/v2/",
-  },
-  Object {
-    "headers": Object {
-      "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json",
-      "accept-encoding": "gzip, deflate, br",
-      "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk",
-      "host": "index.docker.io",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://index.docker.io/v2/library/some-dep/manifests/some-tag",
-  },
-]
-`;
-
-exports[`datasource/docker/index getDigest returns null if empty header 1`] = `
-Array [
-  Object {
-    "headers": Object {
-      "accept-encoding": "gzip, deflate, br",
-      "host": "index.docker.io",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://index.docker.io/v2/",
-  },
-  Object {
-    "headers": Object {
-      "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json",
-      "accept-encoding": "gzip, deflate, br",
-      "host": "index.docker.io",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://index.docker.io/v2/library/some-dep/manifests/some-new-value",
-  },
-]
-`;
-
-exports[`datasource/docker/index getDigest returns null if errored 1`] = `
-Array [
-  Object {
-    "headers": Object {
-      "accept-encoding": "gzip, deflate, br",
-      "host": "index.docker.io",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://index.docker.io/v2/",
-  },
-  Object {
-    "headers": Object {
-      "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json",
-      "accept-encoding": "gzip, deflate, br",
-      "host": "index.docker.io",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://index.docker.io/v2/library/some-dep/manifests/some-new-value",
-  },
-]
-`;
-
-exports[`datasource/docker/index getDigest returns null if no token 1`] = `
-Array [
-  Object {
-    "headers": Object {
-      "accept-encoding": "gzip, deflate, br",
-      "host": "index.docker.io",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://index.docker.io/v2/",
-  },
-  Object {
-    "headers": Object {
-      "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json",
-      "accept-encoding": "gzip, deflate, br",
-      "host": "index.docker.io",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://index.docker.io/v2/library/some-dep/manifests/some-new-value",
-  },
-]
-`;
-
-exports[`datasource/docker/index getDigest supports ECR authentication 1`] = `
-Array [
-  Object {
-    "headers": Object {
-      "accept-encoding": "gzip, deflate, br",
-      "host": "123456789.dkr.ecr.us-east-1.amazonaws.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://123456789.dkr.ecr.us-east-1.amazonaws.com/v2/",
-  },
-  Object {
-    "headers": Object {
-      "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json",
-      "accept-encoding": "gzip, deflate, br",
-      "authorization": "Basic abcdef",
-      "host": "123456789.dkr.ecr.us-east-1.amazonaws.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://123456789.dkr.ecr.us-east-1.amazonaws.com/v2/node/manifests/some-tag",
-  },
-]
-`;
-
-exports[`datasource/docker/index getDigest supports basic authentication 1`] = `
-Array [
-  Object {
-    "headers": Object {
-      "accept-encoding": "gzip, deflate, br",
-      "host": "index.docker.io",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://index.docker.io/v2/",
-  },
-  Object {
-    "headers": Object {
-      "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json",
-      "accept-encoding": "gzip, deflate, br",
-      "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk",
-      "host": "index.docker.io",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://index.docker.io/v2/library/some-dep/manifests/some-tag",
-  },
-]
-`;
-
-exports[`datasource/docker/index getDigest supports docker insecure registry 1`] = `
-Array [
-  Object {
-    "headers": Object {
-      "accept-encoding": "gzip, deflate, br",
-      "host": "index.docker.io",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "http://index.docker.io/v2/",
-  },
-  Object {
-    "headers": Object {
-      "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json",
-      "accept-encoding": "gzip, deflate, br",
-      "host": "index.docker.io",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "http://index.docker.io/v2/library/some-dep/manifests/latest",
-  },
-]
-`;
-
-exports[`datasource/docker/index getDigest supports scoped names 1`] = `
-Array [
-  Object {
-    "headers": Object {
-      "accept-encoding": "gzip, deflate, br",
-      "host": "index.docker.io",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://index.docker.io/v2/",
-  },
-  Object {
-    "headers": Object {
-      "accept": "application/json",
-      "accept-encoding": "gzip, deflate, br",
-      "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk",
-      "host": "auth.docker.io",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://auth.docker.io/token?service=registry.docker.io&scope=repository:library/some-other-dep:pull",
-  },
-  Object {
-    "headers": Object {
-      "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json",
-      "accept-encoding": "gzip, deflate, br",
-      "authorization": "Bearer some-token",
-      "host": "index.docker.io",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://index.docker.io/v2/library/some-other-dep/manifests/8.0.0-alpine",
-  },
-]
-`;
-
-exports[`datasource/docker/index getReleases adds library/ prefix for Docker Hub (explicit) 1`] = `
-Array [
-  Object {
-    "headers": Object {
-      "accept-encoding": "gzip, deflate, br",
-      "host": "index.docker.io",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://index.docker.io/v2/",
-  },
-  Object {
-    "headers": Object {
-      "accept": "application/json",
-      "accept-encoding": "gzip, deflate, br",
-      "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk",
-      "host": "auth.docker.io",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://auth.docker.io/token?service=registry.docker.io&scope=repository:library/node:pull",
-  },
-  Object {
-    "headers": Object {
-      "accept": "application/json",
-      "accept-encoding": "gzip, deflate, br",
-      "authorization": "Bearer some-token ",
-      "host": "index.docker.io",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://index.docker.io/v2/library/node/tags/list?n=10000",
-  },
-  Object {
-    "headers": Object {
-      "accept-encoding": "gzip, deflate, br",
-      "host": "index.docker.io",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://index.docker.io/v2/",
-  },
-  Object {
-    "headers": Object {
-      "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json",
-      "accept-encoding": "gzip, deflate, br",
-      "host": "index.docker.io",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://index.docker.io/v2/library/node/manifests/1.0.0",
-  },
-]
-`;
-
-exports[`datasource/docker/index getReleases adds library/ prefix for Docker Hub (implicit) 1`] = `
-Array [
-  Object {
-    "headers": Object {
-      "accept-encoding": "gzip, deflate, br",
-      "host": "index.docker.io",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://index.docker.io/v2/",
-  },
-  Object {
-    "headers": Object {
-      "accept": "application/json",
-      "accept-encoding": "gzip, deflate, br",
-      "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk",
-      "host": "auth.docker.io",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://auth.docker.io/token?service=registry.docker.io&scope=repository:library/node:pull",
-  },
-  Object {
-    "headers": Object {
-      "accept": "application/json",
-      "accept-encoding": "gzip, deflate, br",
-      "authorization": "Bearer some-token ",
-      "host": "index.docker.io",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://index.docker.io/v2/library/node/tags/list?n=10000",
-  },
-  Object {
-    "headers": Object {
-      "accept-encoding": "gzip, deflate, br",
-      "host": "index.docker.io",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://index.docker.io/v2/",
-  },
-  Object {
-    "headers": Object {
-      "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json",
-      "accept-encoding": "gzip, deflate, br",
-      "host": "index.docker.io",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://index.docker.io/v2/library/node/manifests/1.0.0",
-  },
-]
-`;
-
-exports[`datasource/docker/index getReleases adds no library/ prefix for other registries 1`] = `
-Array [
-  Object {
-    "headers": Object {
-      "accept-encoding": "gzip, deflate, br",
-      "host": "k8s.gcr.io",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://k8s.gcr.io/v2/",
-  },
-  Object {
-    "headers": Object {
-      "accept": "application/json",
-      "accept-encoding": "gzip, deflate, br",
-      "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk",
-      "host": "k8s.gcr.io",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://k8s.gcr.io/v2/token?service=k8s.gcr.io&scope=repository:kubernetes-dashboard-amd64:pull",
-  },
-  Object {
-    "headers": Object {
-      "accept": "application/json",
-      "accept-encoding": "gzip, deflate, br",
-      "authorization": "Bearer some-token ",
-      "host": "k8s.gcr.io",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://k8s.gcr.io/v2/kubernetes-dashboard-amd64/tags/list?n=10000",
-  },
-  Object {
-    "headers": Object {
-      "accept-encoding": "gzip, deflate, br",
-      "host": "k8s.gcr.io",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://k8s.gcr.io/v2/",
-  },
-  Object {
-    "headers": Object {
-      "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json",
-      "accept-encoding": "gzip, deflate, br",
-      "host": "k8s.gcr.io",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://k8s.gcr.io/v2/kubernetes-dashboard-amd64/manifests/1.0.0",
-  },
-]
-`;
-
 exports[`datasource/docker/index getReleases ignores unsupported manifest 1`] = `
 Object {
-  "registryUrl": "https://index.docker.io",
+  "registryUrl": "https://registry.company.com",
   "releases": Array [],
 }
 `;
 
-exports[`datasource/docker/index getReleases ignores unsupported manifest 2`] = `
-Array [
-  Object {
-    "headers": Object {
-      "accept-encoding": "gzip, deflate, br",
-      "host": "registry.company.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://registry.company.com/v2/",
-  },
-  Object {
-    "headers": Object {
-      "accept": "application/json",
-      "accept-encoding": "gzip, deflate, br",
-      "host": "registry.company.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://registry.company.com/v2/node/tags/list?n=10000",
-  },
-  Object {
-    "headers": Object {
-      "accept-encoding": "gzip, deflate, br",
-      "host": "registry.company.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://registry.company.com/v2/",
-  },
-  Object {
-    "headers": Object {
-      "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json",
-      "accept-encoding": "gzip, deflate, br",
-      "host": "registry.company.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://registry.company.com/v2/node/manifests/latest",
-  },
-]
-`;
-
 exports[`datasource/docker/index getReleases ignores unsupported schema version 1`] = `
 Object {
-  "registryUrl": "https://index.docker.io",
+  "registryUrl": "https://registry.company.com",
   "releases": Array [],
 }
 `;
 
-exports[`datasource/docker/index getReleases ignores unsupported schema version 2`] = `
-Array [
-  Object {
-    "headers": Object {
-      "accept-encoding": "gzip, deflate, br",
-      "host": "registry.company.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://registry.company.com/v2/",
-  },
-  Object {
-    "headers": Object {
-      "accept": "application/json",
-      "accept-encoding": "gzip, deflate, br",
-      "host": "registry.company.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://registry.company.com/v2/node/tags/list?n=10000",
-  },
-  Object {
-    "headers": Object {
-      "accept-encoding": "gzip, deflate, br",
-      "host": "registry.company.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://registry.company.com/v2/",
-  },
-  Object {
-    "headers": Object {
-      "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json",
-      "accept-encoding": "gzip, deflate, br",
-      "host": "registry.company.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://registry.company.com/v2/node/manifests/latest",
-  },
-]
-`;
-
-exports[`datasource/docker/index getReleases returns null if no auth 1`] = `
-Array [
-  Object {
-    "headers": Object {
-      "accept-encoding": "gzip, deflate, br",
-      "host": "index.docker.io",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://index.docker.io/v2/",
-  },
-]
-`;
-
-exports[`datasource/docker/index getReleases returns null if no token 1`] = `
-Array [
-  Object {
-    "headers": Object {
-      "accept-encoding": "gzip, deflate, br",
-      "host": "index.docker.io",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://index.docker.io/v2/",
-  },
-  Object {
-    "headers": Object {
-      "accept": "application/json",
-      "accept-encoding": "gzip, deflate, br",
-      "host": "index.docker.io",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://index.docker.io/v2/library/node/tags/list?n=10000",
-  },
-]
-`;
-
-exports[`datasource/docker/index getReleases returns null on error 1`] = `
-Array [
-  Object {
-    "headers": Object {
-      "accept-encoding": "gzip, deflate, br",
-      "host": "index.docker.io",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://index.docker.io/v2/",
-  },
-  Object {
-    "headers": Object {
-      "accept": "application/json",
-      "accept-encoding": "gzip, deflate, br",
-      "host": "index.docker.io",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://index.docker.io/v2/my/node/tags/list?n=10000",
-  },
-]
-`;
-
 exports[`datasource/docker/index getReleases supports labels 1`] = `
 Object {
-  "registryUrl": "https://index.docker.io",
+  "registryUrl": "https://registry.company.com",
   "releases": Array [
     Object {
       "version": "1.0.0",
@@ -709,402 +41,17 @@ Object {
 }
 `;
 
-exports[`datasource/docker/index getReleases supports labels 2`] = `
-Array [
-  Object {
-    "headers": Object {
-      "accept-encoding": "gzip, deflate, br",
-      "host": "registry.company.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://registry.company.com/v2/",
-  },
-  Object {
-    "headers": Object {
-      "accept": "application/json",
-      "accept-encoding": "gzip, deflate, br",
-      "host": "registry.company.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://registry.company.com/v2/node/tags/list?n=10000",
-  },
-  Object {
-    "headers": Object {
-      "accept-encoding": "gzip, deflate, br",
-      "host": "registry.company.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://registry.company.com/v2/",
-  },
-  Object {
-    "headers": Object {
-      "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json",
-      "accept-encoding": "gzip, deflate, br",
-      "host": "registry.company.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://registry.company.com/v2/node/manifests/2-alpine",
-  },
-  Object {
-    "headers": Object {
-      "accept-encoding": "gzip, deflate, br",
-      "host": "registry.company.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://registry.company.com/v2/",
-  },
-  Object {
-    "headers": Object {
-      "accept-encoding": "gzip, deflate, br",
-      "host": "registry.company.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://registry.company.com/v2/node/blobs/some-config-digest",
-  },
-]
-`;
-
 exports[`datasource/docker/index getReleases supports manifest lists 1`] = `
 Object {
-  "registryUrl": "https://index.docker.io",
+  "registryUrl": "https://registry.company.com",
   "releases": Array [],
   "sourceUrl": "https://github.com/renovatebot/renovate",
 }
 `;
 
-exports[`datasource/docker/index getReleases supports manifest lists 2`] = `
-Array [
-  Object {
-    "headers": Object {
-      "accept-encoding": "gzip, deflate, br",
-      "host": "registry.company.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://registry.company.com/v2/",
-  },
-  Object {
-    "headers": Object {
-      "accept": "application/json",
-      "accept-encoding": "gzip, deflate, br",
-      "host": "registry.company.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://registry.company.com/v2/node/tags/list?n=10000",
-  },
-  Object {
-    "headers": Object {
-      "accept-encoding": "gzip, deflate, br",
-      "host": "registry.company.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://registry.company.com/v2/",
-  },
-  Object {
-    "headers": Object {
-      "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json",
-      "accept-encoding": "gzip, deflate, br",
-      "host": "registry.company.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://registry.company.com/v2/node/manifests/abc",
-  },
-  Object {
-    "headers": Object {
-      "accept-encoding": "gzip, deflate, br",
-      "host": "registry.company.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://registry.company.com/v2/",
-  },
-  Object {
-    "headers": Object {
-      "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json",
-      "accept-encoding": "gzip, deflate, br",
-      "host": "registry.company.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://registry.company.com/v2/node/manifests/some-image-digest",
-  },
-  Object {
-    "headers": Object {
-      "accept-encoding": "gzip, deflate, br",
-      "host": "registry.company.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://registry.company.com/v2/",
-  },
-  Object {
-    "headers": Object {
-      "accept-encoding": "gzip, deflate, br",
-      "host": "registry.company.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://registry.company.com/v2/node/blobs/some-config-digest",
-  },
-]
-`;
-
 exports[`datasource/docker/index getReleases supports redirect 1`] = `
 Object {
-  "registryUrl": "https://index.docker.io",
+  "registryUrl": "https://registry.company.com",
   "releases": Array [],
 }
 `;
-
-exports[`datasource/docker/index getReleases supports redirect 2`] = `
-Array [
-  Object {
-    "headers": Object {
-      "accept-encoding": "gzip, deflate, br",
-      "host": "registry.company.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://registry.company.com/v2/",
-  },
-  Object {
-    "headers": Object {
-      "accept": "application/json",
-      "accept-encoding": "gzip, deflate, br",
-      "host": "registry.company.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://registry.company.com/v2/node/tags/list?n=10000",
-  },
-  Object {
-    "headers": Object {
-      "accept-encoding": "gzip, deflate, br",
-      "host": "registry.company.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://registry.company.com/v2/",
-  },
-  Object {
-    "headers": Object {
-      "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json",
-      "accept-encoding": "gzip, deflate, br",
-      "host": "registry.company.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://registry.company.com/v2/node/manifests/latest",
-  },
-  Object {
-    "headers": Object {
-      "accept-encoding": "gzip, deflate, br",
-      "host": "registry.company.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://registry.company.com/v2/",
-  },
-  Object {
-    "headers": Object {
-      "accept-encoding": "gzip, deflate, br",
-      "host": "registry.company.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://registry.company.com/v2/node/blobs/some-config-digest",
-  },
-  Object {
-    "headers": Object {
-      "accept-encoding": "gzip, deflate, br",
-      "host": "abc.s3.amazon.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://abc.s3.amazon.com/some-config-digest?X-Amz-Algorithm=xxxx",
-  },
-]
-`;
-
-exports[`datasource/docker/index getReleases uses custom registry in depName 1`] = `
-Array [
-  Object {
-    "headers": Object {
-      "accept-encoding": "gzip, deflate, br",
-      "host": "registry.company.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://registry.company.com/v2/",
-  },
-  Object {
-    "headers": Object {
-      "accept": "application/json",
-      "accept-encoding": "gzip, deflate, br",
-      "host": "registry.company.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://registry.company.com/v2/node/tags/list?n=10000",
-  },
-  Object {
-    "headers": Object {
-      "accept-encoding": "gzip, deflate, br",
-      "host": "registry.company.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://registry.company.com/v2/",
-  },
-  Object {
-    "headers": Object {
-      "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json",
-      "accept-encoding": "gzip, deflate, br",
-      "host": "registry.company.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://registry.company.com/v2/node/manifests/1.0.0",
-  },
-]
-`;
-
-exports[`datasource/docker/index getReleases uses custom registry with registryUrls 1`] = `
-Array [
-  Object {
-    "headers": Object {
-      "accept-encoding": "gzip, deflate, br",
-      "host": "registry.company.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://registry.company.com/v2/",
-  },
-  Object {
-    "headers": Object {
-      "accept": "application/json",
-      "accept-encoding": "gzip, deflate, br",
-      "host": "registry.company.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://registry.company.com/v2/node/tags/list?n=10000",
-  },
-  Object {
-    "headers": Object {
-      "accept": "application/json",
-      "accept-encoding": "gzip, deflate, br",
-      "host": "api.github.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://api.github.com/user/9287/repos?page=3&per_page=100",
-  },
-  Object {
-    "headers": Object {
-      "accept-encoding": "gzip, deflate, br",
-      "host": "registry.company.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://registry.company.com/v2/",
-  },
-  Object {
-    "headers": Object {
-      "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json",
-      "accept-encoding": "gzip, deflate, br",
-      "host": "registry.company.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://registry.company.com/v2/node/manifests/latest",
-  },
-]
-`;
-
-exports[`datasource/docker/index getReleases uses lower tag limit for ECR deps 1`] = `
-Array [
-  Object {
-    "headers": Object {
-      "accept-encoding": "gzip, deflate, br",
-      "host": "123456789.dkr.ecr.us-east-1.amazonaws.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://123456789.dkr.ecr.us-east-1.amazonaws.com/v2/",
-  },
-  Object {
-    "headers": Object {
-      "accept": "application/json",
-      "accept-encoding": "gzip, deflate, br",
-      "host": "123456789.dkr.ecr.us-east-1.amazonaws.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://123456789.dkr.ecr.us-east-1.amazonaws.com/v2/node/tags/list?n=1000",
-  },
-  Object {
-    "headers": Object {
-      "accept-encoding": "gzip, deflate, br",
-      "host": "123456789.dkr.ecr.us-east-1.amazonaws.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://123456789.dkr.ecr.us-east-1.amazonaws.com/v2/",
-  },
-  Object {
-    "headers": Object {
-      "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json",
-      "accept-encoding": "gzip, deflate, br",
-      "host": "123456789.dkr.ecr.us-east-1.amazonaws.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://123456789.dkr.ecr.us-east-1.amazonaws.com/v2/node/manifests/undefined",
-  },
-]
-`;
-
-exports[`datasource/docker/index getReleases uses quay api 1`] = `
-Array [
-  Object {
-    "headers": Object {
-      "accept": "application/json",
-      "accept-encoding": "gzip, deflate, br",
-      "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk",
-      "host": "quay.io",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://quay.io/api/v1/repository/bitnami/redis/tag/?limit=100&page=1&onlyActiveTags=true",
-  },
-  Object {
-    "headers": Object {
-      "accept-encoding": "gzip, deflate, br",
-      "host": "quay.io",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://quay.io/v2/",
-  },
-  Object {
-    "headers": Object {
-      "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json",
-      "accept-encoding": "gzip, deflate, br",
-      "host": "quay.io",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://quay.io/v2/bitnami/redis/manifests/5.0.12",
-  },
-]
-`;
diff --git a/lib/datasource/docker/common.ts b/lib/datasource/docker/common.ts
index 72cb51214a..3b101a6a25 100644
--- a/lib/datasource/docker/common.ts
+++ b/lib/datasource/docker/common.ts
@@ -260,20 +260,18 @@ function digestFromManifestStr(str: hasha.HashaInput): string {
   return 'sha256:' + hasha(str, { algorithm: 'sha256' });
 }
 
-export function extractDigestFromResponse(
+export function extractDigestFromResponseBody(
   manifestResponse: HttpResponse
 ): string {
-  if (manifestResponse.headers['docker-content-digest'] === undefined) {
-    return digestFromManifestStr(manifestResponse.body);
-  }
-  return manifestResponse.headers['docker-content-digest'] as string;
+  return digestFromManifestStr(manifestResponse.body);
 }
 
 // TODO: debug why quay throws errors (#9612)
 export async function getManifestResponse(
   registryHost: string,
   dockerRepository: string,
-  tag: string
+  tag: string,
+  mode: 'head' | 'get' = 'get'
 ): Promise<HttpResponse> {
   logger.debug(
     `getManifestResponse(${registryHost}, ${dockerRepository}, ${tag})`
@@ -287,7 +285,7 @@ export async function getManifestResponse(
     headers.accept =
       'application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json';
     const url = `${registryHost}/v2/${dockerRepository}/manifests/${tag}`;
-    const manifestResponse = await http.get(url, {
+    const manifestResponse = await http[mode](url, {
       headers,
       noAuth: true,
     });
diff --git a/lib/datasource/docker/index.spec.ts b/lib/datasource/docker/index.spec.ts
index 038fde1a0b..33cd9df8d4 100644
--- a/lib/datasource/docker/index.spec.ts
+++ b/lib/datasource/docker/index.spec.ts
@@ -59,46 +59,47 @@ describe('datasource/docker/index', () => {
     it('returns null if no token', async () => {
       httpMock
         .scope(baseUrl)
-        .get('/')
+        .get('/', undefined, { badheaders: ['authorization'] })
         .reply(200, '', {})
-        .get('/library/some-dep/manifests/some-new-value')
+        .head('/library/some-dep/manifests/some-new-value', undefined, {
+          badheaders: ['authorization'],
+        })
         .reply(401);
       const res = await getDigest(
         { datasource: 'docker', depName: 'some-dep' },
         'some-new-value'
       );
       expect(res).toBeNull();
-      expect(httpMock.getTrace()).toMatchSnapshot();
     });
 
     it('returns null if errored', async () => {
       httpMock
         .scope(baseUrl)
-        .get('/')
-        .reply(200, { token: 'some-token' })
-        .get('/library/some-dep/manifests/some-new-value')
+        .get('/', undefined, { badheaders: ['authorization'] })
+        .reply(200, { token: 'abc' })
+        .head('/library/some-dep/manifests/some-new-value', undefined, {
+          reqheaders: { authorization: 'Bearer abc' },
+        })
         .replyWithError('error');
       const res = await getDigest(
         { datasource: 'docker', depName: 'some-dep' },
         'some-new-value'
       );
       expect(res).toBeNull();
-      expect(httpMock.getTrace()).toMatchSnapshot();
     });
 
     it('returns null if empty header', async () => {
       httpMock
         .scope(baseUrl)
-        .get('/')
+        .get('/', undefined, { badheaders: ['authorization'] })
         .reply(200, { token: 'some-token' })
-        .get('/library/some-dep/manifests/some-new-value')
+        .head('/library/some-dep/manifests/some-new-value')
         .reply(200, undefined, { 'docker-content-digest': '' });
       const res = await getDigest(
         { datasource: 'docker', depName: 'some-dep' },
         'some-new-value'
       );
       expect(res).toBeNull();
-      expect(httpMock.getTrace()).toMatchSnapshot();
     });
 
     it('returns digest', async () => {
@@ -109,7 +110,7 @@ describe('datasource/docker/index', () => {
           'www-authenticate':
             'Bearer realm="https://auth.docker.io/token",service="registry.docker.io",scope="repository:samalba/my-app:pull  "',
         })
-        .get('/library/some-dep/manifests/latest')
+        .head('/library/some-dep/manifests/latest')
         .reply(200, {}, { 'docker-content-digest': 'some-digest' });
       httpMock
         .scope(authUrl)
@@ -124,17 +125,19 @@ describe('datasource/docker/index', () => {
         depName: 'some-dep',
       });
       expect(res).toBe('some-digest');
-      expect(httpMock.getTrace()).toMatchSnapshot();
     });
 
     it('falls back to body for digest', async () => {
       httpMock
         .scope(baseUrl)
         .get('/')
+        .twice()
         .reply(401, '', {
           'www-authenticate':
             'Bearer realm="https://auth.docker.io/token",service="registry.docker.io",scope="repository:samalba/my-app:pull  "',
         })
+        .head('/library/some-dep/manifests/some-new-value')
+        .reply(200, undefined, {})
         .get('/library/some-dep/manifests/some-new-value')
         .reply(
           200,
@@ -165,6 +168,7 @@ describe('datasource/docker/index', () => {
         .get(
           '/token?service=registry.docker.io&scope=repository:library/some-dep:pull'
         )
+        .twice()
         .reply(200, { token: 'some-token' });
       const res = await getDigest(
         { datasource: 'docker', depName: 'some-dep' },
@@ -173,15 +177,14 @@ describe('datasource/docker/index', () => {
       expect(res).toBe(
         'sha256:b3d6068234f3a18ebeedd2dab81e67b6a192e81192a099df4112ecfc7c3be84f'
       );
-      expect(httpMock.getTrace()).toMatchSnapshot();
     });
 
     it('supports docker insecure registry', async () => {
       httpMock
         .scope(baseUrl.replace('https', 'http'))
-        .get('/')
+        .get('/', undefined, { badheaders: ['authorization'] })
         .reply(200)
-        .get('/library/some-dep/manifests/latest')
+        .head('/library/some-dep/manifests/latest')
         .reply(200, '', { 'docker-content-digest': 'some-digest' });
       hostRules.find.mockReturnValueOnce({ insecureRegistry: true });
       const res = await getDigest({
@@ -189,45 +192,42 @@ describe('datasource/docker/index', () => {
         depName: 'some-dep',
       });
       expect(res).toBe('some-digest');
-      expect(httpMock.getTrace()).toMatchSnapshot();
     });
 
     it('supports basic authentication', async () => {
       httpMock
         .scope(baseUrl)
-        .get('/')
+        .get('/', undefined, { badheaders: ['authorization'] })
         .reply(401, '', {
           'www-authenticate': 'Basic realm="My Private Docker Registry Server"',
         })
-        .get('/library/some-dep/manifests/some-tag')
+        .head('/library/some-dep/manifests/some-tag', undefined, {
+          reqheaders: {
+            authorization: 'Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk',
+          },
+        })
         .reply(200, '', { 'docker-content-digest': 'some-digest' });
       const res = await getDigest(
         { datasource: 'docker', depName: 'some-dep' },
         'some-tag'
       );
-      const trace = httpMock.getTrace();
       expect(res).toBe('some-digest');
-      expect(trace[1].headers.authorization).toBe(
-        'Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk'
-      );
-      expect(trace).toMatchSnapshot();
     });
 
     it('returns null for 403 with basic authentication', async () => {
       httpMock
         .scope(baseUrl)
-        .get('/')
+        .get('/', undefined, { badheaders: ['authorization'] })
         .reply(401, '', {
           'www-authenticate': 'Basic realm="My Private Docker Registry Server"',
         })
-        .get('/library/some-dep/manifests/some-tag')
+        .head('/library/some-dep/manifests/some-tag')
         .reply(403);
       const res = await getDigest(
         { datasource: 'docker', depName: 'some-dep' },
         'some-tag'
       );
       expect(res).toBeNull();
-      expect(httpMock.getTrace()).toMatchSnapshot();
     });
 
     it('passes credentials to ECR client', async () => {
@@ -237,7 +237,9 @@ describe('datasource/docker/index', () => {
         .reply(401, '', {
           'www-authenticate': 'Basic realm="My Private Docker Registry Server"',
         })
-        .get('/node/manifests/some-tag')
+        .head('/node/manifests/some-tag', undefined, {
+          reqheaders: { authorization: 'Basic abc' },
+        })
         .reply(200, '', { 'docker-content-digest': 'some-digest' });
 
       mockEcrAuthResolve({
@@ -252,8 +254,6 @@ describe('datasource/docker/index', () => {
         'some-tag'
       );
 
-      const trace = httpMock.getTrace();
-      expect(trace).toMatchSnapshot();
       expect(AWS.ECR).toHaveBeenCalledWith({
         credentials: {
           accessKeyId: 'some-username',
@@ -270,11 +270,13 @@ describe('datasource/docker/index', () => {
         .reply(401, '', {
           'www-authenticate': 'Basic realm="My Private Docker Registry Server"',
         })
-        .get('/node/manifests/some-tag')
+        .head('/node/manifests/some-tag', undefined, {
+          reqheaders: { authorization: 'Basic abc' },
+        })
         .reply(200, '', { 'docker-content-digest': 'some-digest' });
 
       mockEcrAuthResolve({
-        authorizationData: [{ authorizationToken: 'abcdef' }],
+        authorizationData: [{ authorizationToken: 'abc' }],
       });
 
       const res = await getDigest(
@@ -284,10 +286,8 @@ describe('datasource/docker/index', () => {
         },
         'some-tag'
       );
-      const trace = httpMock.getTrace();
+
       expect(res).toBe('some-digest');
-      expect(trace[1].headers.authorization).toBe('Basic abcdef');
-      expect(trace).toMatchSnapshot();
     });
 
     it('continues without token if ECR authentication could not be extracted', async () => {
@@ -304,7 +304,6 @@ describe('datasource/docker/index', () => {
         'some-tag'
       );
       expect(res).toBeNull();
-      expect(httpMock.getTrace()).toMatchSnapshot();
     });
 
     it('continues without token if ECR authentication fails', async () => {
@@ -321,7 +320,6 @@ describe('datasource/docker/index', () => {
         'some-tag'
       );
       expect(res).toBeNull();
-      expect(httpMock.getTrace()).toMatchSnapshot();
     });
 
     it('continues without token, when no header is present', async () => {
@@ -331,14 +329,13 @@ describe('datasource/docker/index', () => {
         .reply(200, '', {
           'content-type': 'text/plain',
         })
-        .get('/library/some-dep/manifests/some-new-value')
+        .head('/library/some-dep/manifests/some-new-value')
         .reply(200, {}, { 'docker-content-digest': 'some-digest' });
       const res = await getDigest(
         { datasource: 'docker', depName: 'some-dep' },
         'some-new-value'
       );
       expect(res).toBe('some-digest');
-      expect(httpMock.getTrace()).toMatchSnapshot();
     });
 
     it('supports scoped names', async () => {
@@ -349,7 +346,7 @@ describe('datasource/docker/index', () => {
           'www-authenticate':
             'Bearer realm="https://auth.docker.io/token",service="registry.docker.io",scope="repository:samalba/my-app:pull  "',
         })
-        .get('/library/some-other-dep/manifests/8.0.0-alpine')
+        .head('/library/some-other-dep/manifests/8.0.0-alpine')
         .reply(200, {}, { 'docker-content-digest': 'some-digest' });
       httpMock
         .scope(authUrl)
@@ -362,7 +359,6 @@ describe('datasource/docker/index', () => {
         '8.0.0-alpine'
       );
       expect(res).toBe('some-digest');
-      expect(httpMock.getTrace()).toMatchSnapshot();
     });
 
     it('should throw error for 429', async () => {
@@ -379,6 +375,7 @@ describe('datasource/docker/index', () => {
       ).rejects.toThrow(EXTERNAL_HOST_ERROR);
     });
   });
+
   describe('getReleases', () => {
     it('returns null if no token', async () => {
       httpMock
@@ -393,7 +390,6 @@ describe('datasource/docker/index', () => {
         registryUrls: ['https://docker.io'],
       });
       expect(res).toBeNull();
-      expect(httpMock.getTrace()).toMatchSnapshot();
     });
 
     it('uses custom registry with registryUrls', async () => {
@@ -425,7 +421,6 @@ describe('datasource/docker/index', () => {
       };
       const res = await getPkgReleases(config);
       expect(res.releases).toHaveLength(1);
-      expect(httpMock.getTrace()).toMatchSnapshot();
     });
 
     it('uses custom registry in depName', async () => {
@@ -445,7 +440,6 @@ describe('datasource/docker/index', () => {
         depName: 'registry.company.com/node',
       });
       expect(res.releases).toHaveLength(1);
-      expect(httpMock.getTrace()).toMatchSnapshot();
     });
 
     it('uses quay api', async () => {
@@ -455,7 +449,11 @@ describe('datasource/docker/index', () => {
         .get(
           '/api/v1/repository/bitnami/redis/tag/?limit=100&page=1&onlyActiveTags=true'
         )
-        .reply(200, { tags, has_additional: false })
+        .reply(200, { tags, has_additional: true })
+        .get(
+          '/api/v1/repository/bitnami/redis/tag/?limit=100&page=2&onlyActiveTags=true'
+        )
+        .reply(200, { tags: [], has_additional: false })
         .get('/v2/')
         .reply(200, '', {})
         .get('/v2/bitnami/redis/manifests/5.0.12')
@@ -467,7 +465,6 @@ describe('datasource/docker/index', () => {
       };
       const res = await getPkgReleases(config);
       expect(res.releases).toHaveLength(1);
-      expect(httpMock.getTrace()).toMatchSnapshot();
     });
 
     it('uses quay api and test error', async () => {
@@ -495,16 +492,20 @@ describe('datasource/docker/index', () => {
         // The  tag limit parameter `n` needs to be limited to 1000 for ECR
         // See https://docs.aws.amazon.com/AmazonECR/latest/APIReference/API_DescribeRepositories.html#ECR-DescribeRepositories-request-maxResults
         .get('/node/tags/list?n=1000')
-        .reply(200, {}, {})
+        .reply(200, { tags: ['some'] }, {})
         .get('/')
         .reply(200, '', {})
-        .get('/node/manifests/undefined')
+        .get('/node/manifests/some')
         .reply(200);
-      await getPkgReleases({
-        datasource: id,
-        depName: '123456789.dkr.ecr.us-east-1.amazonaws.com/node',
+      expect(
+        await getPkgReleases({
+          datasource: id,
+          depName: '123456789.dkr.ecr.us-east-1.amazonaws.com/node',
+        })
+      ).toEqual({
+        registryUrl: 'https://123456789.dkr.ecr.us-east-1.amazonaws.com',
+        releases: [],
       });
-      expect(httpMock.getTrace()).toMatchSnapshot();
     });
 
     it('adds library/ prefix for Docker Hub (implicit)', async () => {
@@ -533,7 +534,6 @@ describe('datasource/docker/index', () => {
         depName: 'node',
       });
       expect(res.releases).toHaveLength(1);
-      expect(httpMock.getTrace()).toMatchSnapshot();
     });
 
     it('adds library/ prefix for Docker Hub (explicit)', async () => {
@@ -562,7 +562,6 @@ describe('datasource/docker/index', () => {
         depName: 'docker.io/node',
       });
       expect(res.releases).toHaveLength(1);
-      expect(httpMock.getTrace()).toMatchSnapshot();
     });
 
     it('adds no library/ prefix for other registries', async () => {
@@ -589,7 +588,6 @@ describe('datasource/docker/index', () => {
         depName: 'k8s.gcr.io/kubernetes-dashboard-amd64',
       });
       expect(res.releases).toHaveLength(1);
-      expect(httpMock.getTrace()).toMatchSnapshot();
     });
 
     it('returns null on error', async () => {
@@ -604,7 +602,6 @@ describe('datasource/docker/index', () => {
         depName: 'my/node',
       });
       expect(res).toBeNull();
-      expect(httpMock.getTrace()).toMatchSnapshot();
     });
 
     it('strips trailing slash from registry', async () => {
@@ -643,7 +640,6 @@ describe('datasource/docker/index', () => {
         depName: 'node',
       });
       expect(res).toBeNull();
-      expect(httpMock.getTrace()).toMatchSnapshot();
     });
 
     it('supports labels', async () => {
@@ -683,9 +679,7 @@ describe('datasource/docker/index', () => {
         datasource: id,
         depName: 'registry.company.com/node',
       });
-      const trace = httpMock.getTrace();
       expect(res).toMatchSnapshot();
-      expect(trace).toMatchSnapshot();
     });
 
     it('supports manifest lists', async () => {
@@ -721,9 +715,7 @@ describe('datasource/docker/index', () => {
         datasource: id,
         depName: 'registry.company.com/node',
       });
-      const trace = httpMock.getTrace();
       expect(res).toMatchSnapshot();
-      expect(trace).toMatchSnapshot();
     });
 
     it('ignores unsupported manifest', async () => {
@@ -743,9 +735,7 @@ describe('datasource/docker/index', () => {
         datasource: id,
         depName: 'registry.company.com/node',
       });
-      const trace = httpMock.getTrace();
       expect(res).toMatchSnapshot();
-      expect(trace).toMatchSnapshot();
     });
 
     it('ignores unsupported schema version', async () => {
@@ -762,17 +752,25 @@ describe('datasource/docker/index', () => {
         datasource: id,
         depName: 'registry.company.com/node',
       });
-      const trace = httpMock.getTrace();
       expect(res).toMatchSnapshot();
-      expect(trace).toMatchSnapshot();
     });
 
     it('supports redirect', async () => {
       httpMock
-        .scope('https://registry.company.com/v2')
+        .scope('https://registry.company.com/v2', {
+          badheaders: ['authorization'],
+        })
         .get('/')
         .times(3)
-        .reply(200)
+        .reply(401, '', {
+          'www-authenticate': 'Basic realm="My Private Docker Registry Server"',
+        });
+      httpMock
+        .scope('https://registry.company.com/v2', {
+          reqheaders: {
+            authorization: 'Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk',
+          },
+        })
         .get('/node/tags/list?n=10000')
         .reply(200, { tags: ['latest'] })
         .get('/node/manifests/latest')
@@ -787,7 +785,7 @@ describe('datasource/docker/index', () => {
             'https://abc.s3.amazon.com/some-config-digest?X-Amz-Algorithm=xxxx',
         });
       httpMock
-        .scope('https://abc.s3.amazon.com')
+        .scope('https://abc.s3.amazon.com', { badheaders: ['authorization'] })
         .get('/some-config-digest')
         .query({ 'X-Amz-Algorithm': 'xxxx' })
         .reply(200, {
@@ -797,11 +795,7 @@ describe('datasource/docker/index', () => {
         datasource: id,
         depName: 'registry.company.com/node',
       });
-      const trace = httpMock.getTrace();
       expect(res).toMatchSnapshot();
-      expect(trace).toMatchSnapshot();
-      expect(trace[1].headers.authorization).toBeUndefined();
-      expect(trace[trace.length - 1].headers.authorization).toBeUndefined();
     });
   });
 });
diff --git a/lib/datasource/docker/index.ts b/lib/datasource/docker/index.ts
index 7bba890ce3..c76659ef57 100644
--- a/lib/datasource/docker/index.ts
+++ b/lib/datasource/docker/index.ts
@@ -3,6 +3,7 @@ import parseLinkHeader from 'parse-link-header';
 import { logger } from '../../logger';
 import { ExternalHostError } from '../../types/errors/external-host-error';
 import * as packageCache from '../../util/cache/package';
+import { hasKey } from '../../util/object';
 import { ensurePathPrefix } from '../../util/url';
 import {
   api as dockerVersioning,
@@ -12,7 +13,7 @@ import type { GetReleasesConfig, ReleaseResult } from '../types';
 import {
   defaultRegistryUrls,
   ecrRegex,
-  extractDigestFromResponse,
+  extractDigestFromResponseBody,
   getAuthHeaders,
   getLabels,
   getManifestResponse,
@@ -191,13 +192,27 @@ export async function getDigest(
     if (cachedResult !== undefined) {
       return cachedResult;
     }
-    const manifestResponse = await getManifestResponse(
+    let manifestResponse = await getManifestResponse(
       registryHost,
       dockerRepository,
-      newTag
+      newTag,
+      'head'
     );
     if (manifestResponse) {
-      digest = extractDigestFromResponse(manifestResponse) || null;
+      if (hasKey('docker-content-digest', manifestResponse.headers)) {
+        digest = manifestResponse.headers['docker-content-digest'] || null;
+      } else {
+        logger.debug(
+          { registryHost },
+          'Missing docker content digest header, pulling full manifest'
+        );
+        manifestResponse = await getManifestResponse(
+          registryHost,
+          dockerRepository,
+          newTag
+        );
+        digest = extractDigestFromResponseBody(manifestResponse);
+      }
       logger.debug({ digest }, 'Got docker digest');
     }
   } catch (err) /* istanbul ignore next */ {
@@ -243,6 +258,7 @@ export async function getReleases({
   }
   const releases = tags.map((version) => ({ version }));
   const ret: ReleaseResult = {
+    registryUrl: registryHost,
     releases,
   };
 
diff --git a/lib/util/http/index.ts b/lib/util/http/index.ts
index 91d706ef91..a60501444f 100644
--- a/lib/util/http/index.ts
+++ b/lib/util/http/index.ts
@@ -142,13 +142,23 @@ export class Http<GetOptions = HttpOptions, PostOptions = HttpPostOptions> {
 
     const cacheKey = crypto
       .createHash('md5')
-      .update('got-' + JSON.stringify({ url, headers: options.headers }))
+      .update(
+        'got-' +
+          JSON.stringify({
+            url,
+            headers: options.headers,
+            method: options.method,
+          })
+      )
       .digest('hex');
 
     let resPromise;
 
     // Cache GET requests unless useCache=false
-    if (options.method === 'get' && options.useCache !== false) {
+    if (
+      ['get', 'head'].includes(options.method) &&
+      options.useCache !== false
+    ) {
       resPromise = memCache.get(cacheKey);
     }
 
-- 
GitLab