diff --git a/core/modules/image/image.module b/core/modules/image/image.module
index 4203045b5952b4b02e8a3c9aa79e935f3a791acb..0d8bd4b1f265a84c3e2e170787c2343e8aef8f1d 100644
--- a/core/modules/image/image.module
+++ b/core/modules/image/image.module
@@ -13,6 +13,7 @@
 use Drupal\field\FieldConfigInterface;
 use Drupal\field\FieldStorageConfigInterface;
 use Drupal\file\FileInterface;
+use Drupal\image\Controller\ImageStyleDownloadController;
 use Drupal\image\Entity\ImageStyle;
 
 /**
@@ -151,6 +152,18 @@ function image_file_download($uri) {
     // Check that the file exists and is an image.
     $image = \Drupal::service('image.factory')->get($uri);
     if ($image->isValid()) {
+      // If the image style converted the extension, it has been added to the
+      // original file, resulting in filenames like image.png.jpeg. So to find
+      // the actual source image, we remove the extension and check if that
+      // image exists.
+      if (!file_exists($original_uri)) {
+        $converted_original_uri = ImageStyleDownloadController::getUriWithoutConvertedExtension($original_uri);
+        if ($converted_original_uri !== $original_uri && file_exists($converted_original_uri)) {
+          // The converted file does exist, use it as the source.
+          $original_uri = $converted_original_uri;
+        }
+      }
+
       // Check the permissions of the original to grant access to this image.
       $headers = \Drupal::moduleHandler()->invokeAll('file_download', [$original_uri]);
       // Confirm there's at least one module granting access and none denying access.
diff --git a/core/modules/image/src/Controller/ImageStyleDownloadController.php b/core/modules/image/src/Controller/ImageStyleDownloadController.php
index 543700115ee00de5c60329108ca064766e3a1c9a..bfc5c985d8dcb3ee1dd004c324f4c39b0af4d9b1 100644
--- a/core/modules/image/src/Controller/ImageStyleDownloadController.php
+++ b/core/modules/image/src/Controller/ImageStyleDownloadController.php
@@ -173,6 +173,24 @@ public function deliver(Request $request, $scheme, ImageStyleInterface $image_st
 
     $headers = [];
 
+    // Don't try to generate file if source is missing.
+    if ($image_uri !== $sample_image_uri && !$this->sourceImageExists($image_uri, $token_is_valid)) {
+      // If the image style converted the extension, it has been added to the
+      // original file, resulting in filenames like image.png.jpeg. So to find
+      // the actual source image, we remove the extension and check if that
+      // image exists.
+      $converted_image_uri = static::getUriWithoutConvertedExtension($image_uri);
+      if ($converted_image_uri !== $image_uri &&
+          $this->sourceImageExists($converted_image_uri, $token_is_valid)) {
+        // The converted file does exist, use it as the source.
+        $image_uri = $converted_image_uri;
+      }
+      else {
+        $this->logger->notice('Source image at %source_image_path not found while trying to generate derivative image at %derivative_path.', ['%source_image_path' => $image_uri, '%derivative_path' => $derivative_uri]);
+        return new Response($this->t('Error generating image, missing source file.'), 404);
+      }
+    }
+
     // If not using a public scheme, let other modules provide headers and
     // control access to the file.
     if (!$is_public) {
@@ -183,28 +201,12 @@ public function deliver(Request $request, $scheme, ImageStyleInterface $image_st
     }
 
     // If it is default sample.png, ignore scheme.
+    // This value swap must be done after hook_file_download is called since
+    // the hooks are expecting a URI, not a file path.
     if ($image_uri === $sample_image_uri) {
       $image_uri = $target;
     }
 
-    // Don't try to generate file if source is missing.
-    if (!$this->sourceImageExists($image_uri, $token_is_valid)) {
-      // If the image style converted the extension, it has been added to the
-      // original file, resulting in filenames like image.png.jpeg. So to find
-      // the actual source image, we remove the extension and check if that
-      // image exists.
-      $path_info = pathinfo(StreamWrapperManager::getTarget($image_uri));
-      $converted_image_uri = sprintf('%s://%s%s%s', $this->streamWrapperManager->getScheme($derivative_uri), $path_info['dirname'], DIRECTORY_SEPARATOR, $path_info['filename']);
-      if (!$this->sourceImageExists($converted_image_uri, $token_is_valid)) {
-        $this->logger->notice('Source image at %source_image_path not found while trying to generate derivative image at %derivative_path.', ['%source_image_path' => $image_uri, '%derivative_path' => $derivative_uri]);
-        return new Response($this->t('Error generating image, missing source file.'), 404);
-      }
-      else {
-        // The converted file does exist, use it as the source.
-        $image_uri = $converted_image_uri;
-      }
-    }
-
     // Don't start generating the image if the derivative already exists or if
     // generation is in progress in another thread.
     if (!file_exists($derivative_uri)) {
@@ -283,4 +285,31 @@ private function sourceImageExists(string $image_uri, bool $token_is_valid): boo
     return TRUE;
   }
 
+  /**
+   * Get the file URI without the extension from any conversion image style.
+   *
+   * If the image style converted the image, then an extension has been added
+   * to the original file, resulting in filenames like image.png.jpeg.
+   *
+   * @param string $uri
+   *   The file URI.
+   *
+   * @return string
+   *   The file URI without the extension from any conversion image style.
+   */
+  public static function getUriWithoutConvertedExtension(string $uri): string {
+    $original_uri = $uri;
+    $path_info = pathinfo(StreamWrapperManager::getTarget($uri));
+    // Only convert the URI when the filename still has an extension.
+    if (!empty($path_info['filename']) && pathinfo($path_info['filename'], PATHINFO_EXTENSION)) {
+      $original_uri = StreamWrapperManager::getScheme($uri) . '://';
+      if (!empty($path_info['dirname']) && $path_info['dirname'] !== '.') {
+        $original_uri .= $path_info['dirname'] . DIRECTORY_SEPARATOR;
+      }
+      $original_uri .= $path_info['filename'];
+    }
+
+    return $original_uri;
+  }
+
 }
diff --git a/core/modules/image/tests/src/Functional/ImageStylesPathAndUrlTest.php b/core/modules/image/tests/src/Functional/ImageStylesPathAndUrlTest.php
index 306684fbe68415838dd1677a8c7a7ae2442d7554..f82e52e39608ba1ea9754a1220d00f6f7d0d901b 100644
--- a/core/modules/image/tests/src/Functional/ImageStylesPathAndUrlTest.php
+++ b/core/modules/image/tests/src/Functional/ImageStylesPathAndUrlTest.php
@@ -122,6 +122,22 @@ public function testImageStyleUrlExtraSlash() {
     $this->doImageStyleUrlAndPathTests('public', TRUE, TRUE);
   }
 
+  /**
+   * Test an image style URL with a private file that also gets converted.
+   */
+  public function testImageStylePrivateWithConversion(): void {
+    // Add the "convert" image style effect to our style.
+    $this->style->addImageEffect([
+      'uuid' => '',
+      'id' => 'image_convert',
+      'weight' => 1,
+      'data' => [
+        'extension' => 'jpeg',
+      ],
+    ]);
+    $this->doImageStyleUrlAndPathTests('private');
+  }
+
   /**
    * Tests that an invalid source image returns a 404.
    */