diff --git a/lib/datasource/maven/index.spec.ts b/lib/datasource/maven/index.spec.ts
index a15ee49ff762c47cb82f434b9c4b80fca22d2296..0bb37673b382bf957568207a8227d34d170850a8 100644
--- a/lib/datasource/maven/index.spec.ts
+++ b/lib/datasource/maven/index.spec.ts
@@ -7,15 +7,7 @@ import { getPkgReleases } from '..';
 import { DATASOURCE_FAILURE } from '../../constants/error-messages';
 import * as hostRules from '../../util/host-rules';
 
-const MYSQL_VERSIONS = [
-  '6.0.5',
-  '6.0.6',
-  '8.0.7',
-  '8.0.8',
-  '8.0.9',
-  '8.0.11',
-  '8.0.12',
-];
+const MYSQL_VERSIONS = ['6.0.5', '6.0.6', '8.0.7', '8.0.8', '8.0.9'];
 
 const MYSQL_MAVEN_METADATA = fs.readFileSync(
   resolve(
@@ -46,6 +38,9 @@ describe('datasource/maven', () => {
       username: 'username',
       password: 'password',
     });
+    jest.resetAllMocks();
+    global.repoCache = {};
+    nock.cleanAll();
     nock.disableNetConnect();
     nock('https://repo.maven.apache.org')
       .get('/maven2/mysql/mysql-connector-java/maven-metadata.xml')
@@ -88,6 +83,22 @@ describe('datasource/maven', () => {
         '/maven2/mysql/mysql-connector-java/8.0.12/mysql-connector-java-8.0.12.pom?X-Amz-Algorithm=AWS4-HMAC-SHA256'
       )
       .reply(200, MYSQL_MAVEN_MYSQL_POM);
+    Object.entries({
+      '6.0.5': 200,
+      '6.0.6': 200,
+      '8.0.7': 200,
+      '8.0.8': 200,
+      '8.0.9': 200,
+      '8.0.11': 404,
+      '8.0.12': 500,
+    }).forEach(([v, status]) => {
+      const path = `/maven2/mysql/mysql-connector-java/${v}/mysql-connector-java-${v}.jar`;
+      nock('https://repo.maven.apache.org').head(path).reply(status, '', {});
+      nock('http://frontend_for_private_s3_repository')
+        .head(path)
+        .reply(status, '', {});
+    });
+    return global.renovateCache.rmAll();
   });
 
   afterEach(() => {
@@ -144,7 +155,7 @@ describe('datasource/maven', () => {
         ],
       });
       expect(releases.releases).toEqual(
-        generateReleases(['6.0.4', ...MYSQL_VERSIONS])
+        generateReleases(['6.0.4', ...MYSQL_VERSIONS, '8.0.11', '8.0.12'])
       );
     });
 
diff --git a/lib/datasource/maven/index.ts b/lib/datasource/maven/index.ts
index e4edde6b69caabc99fb2af541322f4c28d31993a..ff7920bedc33feb6a8ba0892df1162a94a7d6654 100644
--- a/lib/datasource/maven/index.ts
+++ b/lib/datasource/maven/index.ts
@@ -1,10 +1,11 @@
 import url from 'url';
 import fs from 'fs-extra';
 import { XmlDocument } from 'xmldoc';
+import pAll from 'p-all';
 import { logger } from '../../logger';
 import { compare } from '../../versioning/maven/compare';
 import mavenVersion from '../../versioning/maven';
-import { downloadHttpProtocol } from './util';
+import { downloadHttpProtocol, isHttpResourceExists } from './util';
 import { GetReleasesConfig, ReleaseResult } from '../common';
 import { MAVEN_REPO } from './common';
 
@@ -43,6 +44,7 @@ function getMavenUrl(
 async function downloadMavenXml(
   pkgUrl: url.URL | null
 ): Promise<XmlDocument | null> {
+  /* istanbul ignore if */
   if (!pkgUrl) {
     return null;
   }
@@ -143,6 +145,112 @@ function extractVersions(metadata: XmlDocument): string[] {
   return elements.map((el) => el.val);
 }
 
+async function getVersionsFromMetadata(
+  dependency: MavenDependency,
+  repoUrl: string
+): Promise<string[] | null> {
+  const metadataUrl = getMavenUrl(dependency, repoUrl, 'maven-metadata.xml');
+  if (!metadataUrl) {
+    return null;
+  }
+
+  const cacheNamespace = 'datasource-maven-metadata';
+  const cacheKey = metadataUrl.toString();
+  const cachedVersions = await renovateCache.get<string[]>(
+    cacheNamespace,
+    cacheKey
+  );
+  /* istanbul ignore if */
+  if (cachedVersions) {
+    return cachedVersions;
+  }
+
+  const mavenMetadata = await downloadMavenXml(metadataUrl);
+  if (!mavenMetadata) {
+    return null;
+  }
+
+  const versions = extractVersions(mavenMetadata);
+  await renovateCache.set<string[]>(cacheNamespace, cacheKey, versions, 10);
+  return versions;
+}
+
+type ArtifactsInfo = Record<string, boolean | null>;
+
+function isValidArtifactsInfo(
+  info: ArtifactsInfo | null,
+  versions: string[]
+): boolean {
+  if (!info) {
+    return false;
+  }
+  return versions.every((v) => info[v] !== undefined);
+}
+
+type ArtifactInfoResult = [string, boolean | null];
+
+async function getArtifactInfo(
+  version: string,
+  artifactUrl: url.URL
+): Promise<ArtifactInfoResult> {
+  const proto = artifactUrl.protocol;
+  if (proto === 'http:' || proto === 'https:') {
+    const result = await isHttpResourceExists(artifactUrl);
+    return [version, result];
+  }
+  return [version, true];
+}
+
+async function filterMissingArtifacts(
+  dependency: MavenDependency,
+  repoUrl: string,
+  versions: string[]
+): Promise<string[]> {
+  const cacheNamespace = 'datasource-maven-metadata';
+  const cacheKey = dependency.dependencyUrl;
+  let artifactsInfo: ArtifactsInfo | null = await renovateCache.get<
+    ArtifactsInfo
+  >(cacheNamespace, cacheKey);
+
+  if (!isValidArtifactsInfo(artifactsInfo, versions)) {
+    const queue = versions
+      .map((version): [string, url.URL | null] => {
+        const artifactUrl = getMavenUrl(
+          dependency,
+          repoUrl,
+          `${version}/${dependency.name}-${version}.jar`
+        );
+        return [version, artifactUrl];
+      })
+      .filter(([_, artifactUrl]) => Boolean(artifactUrl))
+      .map(([version, artifactUrl]) => (): Promise<ArtifactInfoResult> =>
+        getArtifactInfo(version, artifactUrl)
+      );
+    const results = await pAll(queue, { concurrency: 5 });
+    artifactsInfo = results.reduce(
+      (acc, [key, value]) => ({
+        ...acc,
+        [key]: value,
+      }),
+      {}
+    );
+
+    // Retry earlier for status other than 404
+    const cacheTTL = Object.values(artifactsInfo).some((x) => x === null)
+      ? 60
+      : 24 * 60;
+
+    await renovateCache.set<ArtifactsInfo>(
+      cacheNamespace,
+      cacheKey,
+      artifactsInfo,
+      cacheTTL
+    );
+  }
+
+  return versions.filter((v) => artifactsInfo[v]);
+}
+
 export async function getReleases({
   lookupName,
   registryUrls,
@@ -158,18 +266,24 @@ export async function getReleases({
     logger.debug(
       `Looking up ${dependency.display} in repository #${i} - ${repoUrl}`
     );
-    const metadataUrl = getMavenUrl(dependency, repoUrl, 'maven-metadata.xml');
-    const mavenMetadata = await downloadMavenXml(metadataUrl);
-    if (mavenMetadata) {
-      const newVersions = extractVersions(mavenMetadata).filter(
+    const metadataVersions = await getVersionsFromMetadata(dependency, repoUrl);
+    if (metadataVersions) {
+      const availableVersions = await filterMissingArtifacts(
+        dependency,
+        repoUrl,
+        metadataVersions
+      );
+      const filteredVersions = availableVersions.filter(
         (version) => !versions.includes(version)
       );
-      const latestVersion = getLatestStableVersion(newVersions);
+      versions.push(...filteredVersions);
+
+      const latestVersion = getLatestStableVersion(filteredVersions);
       if (latestVersion) {
         repoForVersions[latestVersion] = repoUrl;
       }
-      versions.push(...newVersions);
-      logger.debug(`Found ${newVersions.length} new versions for ${dependency.display} in repository ${repoUrl}`); // prettier-ignore
+
+      logger.debug(`Found ${availableVersions.length} new versions for ${dependency.display} in repository ${repoUrl}`); // prettier-ignore
     }
   }
 
diff --git a/lib/datasource/maven/util.ts b/lib/datasource/maven/util.ts
index 7fa24e30430adde01f1d38343b32d8f18a2fe7f2..9bf3506892c5de1d0d622768ee218ae9b97814b8 100644
--- a/lib/datasource/maven/util.ts
+++ b/lib/datasource/maven/util.ts
@@ -61,6 +61,7 @@ export async function downloadHttpProtocol(
   try {
     const httpClient = httpByHostType(hostType);
     raw = await httpClient.get(pkgUrl.toString());
+    return raw.body;
   } catch (err) {
     const failedUrl = pkgUrl.toString();
     if (isNotFoundError(err)) {
@@ -89,5 +90,23 @@ export async function downloadHttpProtocol(
     }
     return null;
   }
-  return raw.body;
+}
+
+export async function isHttpResourceExists(
+  pkgUrl: url.URL | string,
+  hostType = id
+): Promise<boolean | null> {
+  try {
+    const httpClient = httpByHostType(hostType);
+    await httpClient.head(pkgUrl.toString());
+    return true;
+  } catch (err) {
+    if (isNotFoundError(err)) {
+      return false;
+    }
+
+    const failedUrl = pkgUrl.toString();
+    logger.debug({ failedUrl }, `Can't check HTTP resource existence`);
+    return null;
+  }
 }
diff --git a/lib/util/http/index.ts b/lib/util/http/index.ts
index 0cd6819b488733dc431fc70abefeb5a60ab364ad..e7eb4b97dec15c05f9271036c51f8544468fe4c6 100644
--- a/lib/util/http/index.ts
+++ b/lib/util/http/index.ts
@@ -74,6 +74,10 @@ export class Http {
     return this.request<string>(url, options);
   }
 
+  head(url: string, options: HttpOptions = {}): Promise<HttpResponse> {
+    return this.request<string>(url, { ...options, method: 'head' });
+  }
+
   private async requestJson<T = unknown>(
     url: string,
     options: InternalHttpOptions