diff --git a/config/vufind/config.ini b/config/vufind/config.ini
index 1f522830d1e97c25355a814cea110834775a0383..8bff5f8b935436c624081648fe4811e12425d8b8 100644
--- a/config/vufind/config.ini
+++ b/config/vufind/config.ini
@@ -852,6 +852,11 @@ coverimagesCache = true
 ; default; to enable it, just uncomment the line below.
 ;ajaxcovers = true
 
+; When ajaxcovers is set to true, this setting controls whether the AJAX Handler
+; GetRecordCover renders a fallback template (record/coverReplacement.phtml) in case
+; no cover image could be loaded.
+;useCoverFallbacksOnFail = false
+
 ; These settings control the image to display when no book cover is available.
 ; If makeDynamicCovers is not false and the GD library is installed, VuFind will draw
 ; cover images on the fly. See [DynamicCovers] below for more settings. If set to
diff --git a/module/VuFind/src/VuFind/AjaxHandler/GetRecordCover.php b/module/VuFind/src/VuFind/AjaxHandler/GetRecordCover.php
index c49a7fd39f97b45c8d17e074501d2de6585804f3..9d4d3814d14db5977d485714ca1ae5be065f5032 100644
--- a/module/VuFind/src/VuFind/AjaxHandler/GetRecordCover.php
+++ b/module/VuFind/src/VuFind/AjaxHandler/GetRecordCover.php
@@ -28,6 +28,7 @@
 namespace VuFind\AjaxHandler;
 
 use Laminas\Mvc\Controller\Plugin\Params;
+use Laminas\View\Renderer\PhpRenderer;
 use VuFind\Cover\Router as CoverRouter;
 use VuFind\Exception\RecordMissing as RecordMissingException;
 use VuFind\ILS\Driver\CacheTrait;
@@ -60,17 +61,40 @@ class GetRecordCover extends AbstractBase implements AjaxHandlerInterface
      */
     protected $coverRouter;
 
+    /**
+     * PHP renderer
+     *
+     * @var PhpRenderer
+     */
+    protected $renderer;
+
+    /**
+     * If true we will render a fallback html template in case no image could be
+     * loaded
+     *
+     * @var bool
+     */
+    protected $useCoverFallbacksOnFail = false;
+
     /**
      * GetRecordCover constructor.
      *
-     * @param RecordLoader $recordLoader Record loader
-     * @param CoverRouter  $coverRouter  Cover router
+     * @param RecordLoader $recordLoader            Record loader
+     * @param CoverRouter  $coverRouter             Cover router
+     * @param PhpRenderer  $renderer                PHP renderer (only required if
+     * $userCoverFallbacksOnFail is set to true)
+     * @param bool         $useCoverFallbacksOnFail If true we will render a
+     * fallback html template in case no image could be loaded
      */
     public function __construct(RecordLoader $recordLoader,
-        CoverRouter $coverRouter
+        CoverRouter $coverRouter,
+        ?PhpRenderer $renderer = null,
+        $useCoverFallbacksOnFail = false
     ) {
         $this->recordLoader = $recordLoader;
         $this->coverRouter = $coverRouter;
+        $this->renderer = $renderer;
+        $this->useCoverFallbacksOnFail = $useCoverFallbacksOnFail;
     }
 
     /**
@@ -102,11 +126,19 @@ class GetRecordCover extends AbstractBase implements AjaxHandlerInterface
             );
         }
 
-        return $this->formatResponse(
-            [
-                'url' => $this->coverRouter->getUrl($record, $size ?? 'small'),
-                'size' => $size,
-            ]
+        $url = $this->coverRouter->getUrl(
+            $record, $size ?? 'small', true, $this->useCoverFallbacksOnFail
         );
+
+        return ($url || !$this->renderer || !$this->useCoverFallbacksOnFail)
+            ? $this->formatResponse(compact('url', 'size'))
+            : $this->formatResponse(
+                [
+                    'html' => $this->renderer->render(
+                        'record/coverReplacement',
+                        ['driver' => $record]
+                    )
+                ]
+            );
     }
 }
diff --git a/module/VuFind/src/VuFind/AjaxHandler/GetRecordCoverFactory.php b/module/VuFind/src/VuFind/AjaxHandler/GetRecordCoverFactory.php
index ba99980495c29249ef504569baad84d337eceaa6..b66289308b0fb29a75c1dbca1692bfbe69c22f1b 100644
--- a/module/VuFind/src/VuFind/AjaxHandler/GetRecordCoverFactory.php
+++ b/module/VuFind/src/VuFind/AjaxHandler/GetRecordCoverFactory.php
@@ -63,9 +63,15 @@ class GetRecordCoverFactory implements FactoryInterface
     public function __invoke(ContainerInterface $container, $requestedName,
         array $options = null
     ) {
+        $config
+            = $container->get(\VuFind\Config\PluginManager::class)->get('config');
+        $useFallbacks = $config->Content->useCoverFallbacksOnFail ?? false;
         return new $requestedName(
             $container->get(\VuFind\Record\Loader::class),
-            $container->get(\VuFind\Cover\Router::class)
+            $container->get(\VuFind\Cover\Router::class),
+            // We only need the view renderer if we're going to use fallbacks:
+            $useFallbacks ? $container->get('ViewRenderer') : null,
+            $useFallbacks
         );
     }
 }
diff --git a/module/VuFind/src/VuFind/Cover/Loader.php b/module/VuFind/src/VuFind/Cover/Loader.php
index 3ebcd631988ceff2ff9ed2b915a4e8a1eafb3c99..d4c04ad43d77bebccf3febe71cd436eaa45b6f5c 100644
--- a/module/VuFind/src/VuFind/Cover/Loader.php
+++ b/module/VuFind/src/VuFind/Cover/Loader.php
@@ -164,6 +164,13 @@ class Loader extends \VuFind\ImageLoader
      */
     protected $type;
 
+    /**
+     * Flag denoting the last loaded image was a FailImage
+     *
+     * @var bool
+     */
+    protected $hasLoadedUnavailable = false;
+
     /**
      * Constructor
      *
@@ -315,6 +322,8 @@ class Loader extends \VuFind\ImageLoader
      */
     public function loadImage($settings = [])
     {
+        // reset to normal
+        $this->hasLoadedUnavailable = false;
         // Load settings from legacy function parameters if they are not passed
         // in as an array:
         $settings = is_array($settings)
@@ -343,6 +352,28 @@ class Loader extends \VuFind\ImageLoader
         }
     }
 
+    /**
+     * {@inheritdoc}
+     * Adds @see self::$hasLoadedUnavailable flag
+     *
+     * @return void
+     */
+    public function loadUnavailable()
+    {
+        $this->hasLoadedUnavailable = true;
+        return parent::loadUnavailable();
+    }
+
+    /**
+     * Returns true if the last loaded image was the FailImage
+     *
+     * @return bool
+     */
+    public function hasLoadedUnavailable()
+    {
+        return $this->hasLoadedUnavailable;
+    }
+
     /**
      * Support method for fetchFromAPI() -- set the localFile property.
      *
diff --git a/module/VuFind/src/VuFind/Cover/Router.php b/module/VuFind/src/VuFind/Cover/Router.php
index b96bfa4e10b47e6a57ec52284b9543c0b6b8752a..fdeabb536191ae39408fde27eadf55cff4cf31f2 100644
--- a/module/VuFind/src/VuFind/Cover/Router.php
+++ b/module/VuFind/src/VuFind/Cover/Router.php
@@ -78,11 +78,13 @@ class Router implements \Laminas\Log\LoggerAwareInterface
      * small is default).
      * @param bool         $resolveDynamic Should we resolve dynamic cover data into
      * a URL (true) or simply return false (false)?
+     * @param bool         $testLoadImage  If true the function will try to load the
+     * cover image in advance and returns false in case no image could be loaded
      *
      * @return string|bool
      */
     public function getUrl(RecordDriver $driver, $size = 'small',
-        $resolveDynamic = true
+        $resolveDynamic = true, $testLoadImage = false
     ) {
         // Try to build thumbnail:
         $thumb = $driver->tryMethod('getThumbnail', [$size]);
@@ -127,6 +129,19 @@ class Router implements \Laminas\Log\LoggerAwareInterface
                 );
             }
         }
-        return $directUrl ?? $dynamicUrl ?? false;
+
+        if (isset($directUrl)) {
+            return $directUrl;
+        } elseif (isset($dynamicUrl)) {
+            if ($testLoadImage) {
+                $this->coverLoader->loadImage($settings);
+                if ($this->coverLoader->hasLoadedUnavailable()) {
+                    return false;
+                }
+            }
+            return $dynamicUrl;
+        }
+
+        return false;
     }
 }
diff --git a/themes/bootstrap3/js/covers.js b/themes/bootstrap3/js/covers.js
index fa8b46f1dfb1cb3d62c8321f4d4df73813ef4396..4a9b58c1fabb4bca25c16b6391263f39522613bc 100644
--- a/themes/bootstrap3/js/covers.js
+++ b/themes/bootstrap3/js/covers.js
@@ -7,9 +7,16 @@ function loadCoverByElement(data, element) {
   function coverCallback(response) {
     spinner.hide();
     container.show();
-    if (response.data.url !== false) {
+    if (typeof response.data.url !== 'undefined' && response.data.url !== false) {
       img.attr("src", response.data.url);
       container.children().not("img").hide();
+    } else {
+      img.remove();
+      if (typeof response.data.html !== 'undefined') {
+        container.html(response.data.html);
+      } else {
+        container.html();
+      }
     }
   }
   $.ajax({
diff --git a/themes/bootstrap3/templates/record/coverReplacement.phtml b/themes/bootstrap3/templates/record/coverReplacement.phtml
new file mode 100644
index 0000000000000000000000000000000000000000..6abe01a3db6eb68f47ec211d432aa0ff69847644
--- /dev/null
+++ b/themes/bootstrap3/templates/record/coverReplacement.phtml
@@ -0,0 +1 @@
+<i class="fa fa-question"></i>
\ No newline at end of file