diff --git a/core/modules/image/image.api.php b/core/modules/image/image.api.php
index cb56a76..22276e2 100644
--- a/core/modules/image/image.api.php
+++ b/core/modules/image/image.api.php
@@ -39,5 +39,38 @@ function hook_image_style_flush($style) {
 }
 
 /**
+ * Alters the derivative URI.
+ *
+ * This hook allows modules to alter the URI of the derivative. By implementing
+ * this hook, a module can decide, for example, that a specific image style will
+ * be available in a different stream wrapper. This hook is called when the
+ * image module builds the URI of an image derivative.
+ *
+ * Note: If you use a special URI alteration that stores the image derivatives
+ * outside of public://styles/{image_style}, private://styles/{image_style} or
+ * even outside Drupal (for example, on a public image service like Flickr,
+ * Instagram, etc.), you need to implement your own flush policy. Implementing
+ * hook_image_style_flush() is a good point where you can define your custom
+ * flush actions.
+ *
+ * @param string $uri
+ *   The image style URI to be altered.
+ * @param array $context
+ *   A context associative array with next keys and values:
+ *   - scheme: The image scheme.
+ *   - path: The image path. If this image style changes the extension of the
+ *     derivative, the value of 'path' item reflects this.
+ *   - image_style: The \Drupal\image\ImageStyleInterface image style object.
+ */
+function hook_image_style_uri_alter(&$uri, array $context) {
+  if (($name = $context['image_style']->id()) === 'watermarked_low_resolution') {
+    $path = $context['path'];
+    // Low resolution, watermarked images are available in the
+    // stream wrapper which are writeable.
+    $uri = "public://styles/$name/public/$path";
+  }
+}
+
+/**
  * @} End of "addtogroup hooks".
  */
diff --git a/core/modules/image/src/Entity/ImageStyle.php b/core/modules/image/src/Entity/ImageStyle.php
index dd26124..5e2c4ae 100644
--- a/core/modules/image/src/Entity/ImageStyle.php
+++ b/core/modules/image/src/Entity/ImageStyle.php
@@ -6,6 +6,7 @@
 use Drupal\Core\Config\Entity\ConfigEntityBase;
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Entity\EntityWithPluginCollectionInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Routing\RequestHelper;
 use Drupal\Core\Site\Settings;
 use Drupal\Core\Url;
@@ -85,6 +86,28 @@ class ImageStyle extends ConfigEntityBase implements ImageStyleInterface, Entity
   protected $effectsCollection;
 
   /**
+   * The module handler service.
+   *
+   * @var \Drupal\Core\Extension\ModuleHandlerInterface
+   */
+  protected $moduleHandler;
+
+  /**
+   * Constructs an image style object.
+   *
+   * @param array $values
+   *   An array of values to set, keyed by property name.
+   * @param string $entity_type
+   *   The type of the entity to create.
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler service.
+   */
+  public function __construct(array $values, $entity_type, ModuleHandlerInterface $module_handler = NULL) {
+    parent::__construct($values, $entity_type);
+    $this->moduleHandler = $module_handler ?: \Drupal::moduleHandler();
+  }
+
+  /**
    * {@inheritdoc}
    */
   public function id() {
@@ -185,7 +208,17 @@ public function buildUri($uri) {
       $path = $uri;
       $source_scheme = $scheme = $default_scheme;
     }
-    return "$scheme://styles/{$this->id()}/$source_scheme/{$this->addExtension($path)}";
+
+    // Append a different extension to the image file name, if the image style
+    // specifies so.
+    $path = $this->addExtension($path);
+    $uri = "$scheme://styles/{$this->id()}/$source_scheme/$path";
+
+    // Allow modules to alter the URI before it is returned.
+    $context = ['scheme' => $scheme, 'path' => $path, 'image_style' => $this];
+    $this->moduleHandler->alter('image_style_uri', $uri, $context);
+
+    return $uri;
   }
 
   /**
diff --git a/core/modules/image/src/Tests/ImageStylesPathAndUrlTest.php b/core/modules/image/src/Tests/ImageStylesPathAndUrlTest.php
index baa4e7f..178c980 100644
--- a/core/modules/image/src/Tests/ImageStylesPathAndUrlTest.php
+++ b/core/modules/image/src/Tests/ImageStylesPathAndUrlTest.php
@@ -265,4 +265,20 @@ public function doImageStyleUrlAndPathTests($scheme, $clean_url = TRUE, $extra_s
     $this->assertFalse(file_exists($directory), 'New directory was not created in the filesystem when requesting an unauthorized image.');
   }
 
+  /**
+   * Tests hook_image_style_uri_alter().
+   */
+  public function testImageStyleUriAlter() {
+    $style = ImageStyle::create([
+      'name' => 'watermarked_low_resolution',
+      'label' => $this->randomString(),
+    ]);
+    $style->save();
+
+    $original = 'private://bar/baz.png';
+    // The 'watermarked_low_resolution' derivatives are available publicly, in
+    // the stream wrapper.
+    $this->assertIdentical($style->buildUri($original), 'public://styles/watermarked_low_resolution/public/bar/baz.png');
+  }
+
 }
diff --git a/core/modules/image/tests/modules/image_module_test/image_module_test.module b/core/modules/image/tests/modules/image_module_test/image_module_test.module
index 20c515e..017b0e5 100644
--- a/core/modules/image/tests/modules/image_module_test/image_module_test.module
+++ b/core/modules/image/tests/modules/image_module_test/image_module_test.module
@@ -32,3 +32,15 @@ function image_module_test_image_effect_info_alter(&$effects) {
 function image_module_test_image_style_presave(ImageStyleInterface $style) {
   $style->setThirdPartySetting('image_module_test', 'foo', 'bar');
 }
+
+/**
+ * Implements hook_image_style_uri_alter().
+ */
+function image_module_test_image_style_uri_alter(&$uri, array $context) {
+  if (($name = $context['image_style']->id()) === 'watermarked_low_resolution') {
+    $path = $context['path'];
+    // Low resolution, watermarked images are available publicly, in the
+    // public:// stream wrapper.
+    $uri = "public://styles/$name/public/$path";
+  }
+}
diff --git a/core/modules/image/tests/src/Unit/ImageStyleTest.php b/core/modules/image/tests/src/Unit/ImageStyleTest.php
index bc954b3..c60cd04 100644
--- a/core/modules/image/tests/src/Unit/ImageStyleTest.php
+++ b/core/modules/image/tests/src/Unit/ImageStyleTest.php
@@ -58,10 +58,14 @@ protected function getImageStyleMock($image_effect_id, $image_effect, $stubs = [
       'fileUriTarget',
       'fileDefaultScheme',
     ];
+
+    $module_handler = $this->getMockBuilder('\Drupal\Core\Extension\ModuleHandlerInterface::class')
+      ->getMock();
     $image_style = $this->getMockBuilder('\Drupal\image\Entity\ImageStyle')
       ->setConstructorArgs([
         ['effects' => [$image_effect_id => ['id' => $image_effect_id]]],
         $this->entityTypeId,
+        $module_handler,
       ])
       ->setMethods(array_merge($default_stubs, $stubs))
       ->getMock();
