diff --git a/typo3/sysext/core/Classes/Resource/Driver/AbstractDriver.php b/typo3/sysext/core/Classes/Resource/Driver/AbstractDriver.php
index c5d8bfdd9d5e78fd597ebd6cf16b896b83003647..487399b8b50c437a7c7cd1933f0981c7d6d76248 100644
--- a/typo3/sysext/core/Classes/Resource/Driver/AbstractDriver.php
+++ b/typo3/sysext/core/Classes/Resource/Driver/AbstractDriver.php
@@ -117,7 +117,7 @@ abstract class AbstractDriver implements DriverInterface
      */
     public function hasCapability($capability)
     {
-        return ($this->capabilities & $capability) == $capability;
+        return ($this->capabilities & $capability) === $capability;
     }
 
     /*******************
diff --git a/typo3/sysext/core/Classes/Resource/Driver/LocalDriver.php b/typo3/sysext/core/Classes/Resource/Driver/LocalDriver.php
index 0114b94b181ca216bf4ec0d7ea743c47d58b97d3..afb2ee6c9f335190e5bdd38fd89b8b27278c83cd 100644
--- a/typo3/sysext/core/Classes/Resource/Driver/LocalDriver.php
+++ b/typo3/sysext/core/Classes/Resource/Driver/LocalDriver.php
@@ -131,7 +131,9 @@ class LocalDriver extends AbstractHierarchicalFilesystemDriver implements Stream
     {
         // only calculate baseURI if the storage does not enforce jumpUrl Script
         if ($this->hasCapability(ResourceStorage::CAPABILITY_PUBLIC)) {
-            if (GeneralUtility::isFirstPartOfStr($this->absoluteBasePath, Environment::getPublicPath())) {
+            if (!empty($this->configuration['baseUri'])) {
+                $this->baseUri = rtrim($this->configuration['baseUri'], '/') . '/';
+            } elseif (GeneralUtility::isFirstPartOfStr($this->absoluteBasePath, Environment::getPublicPath())) {
                 // use site-relative URLs
                 $temporaryBaseUri = rtrim(PathUtility::stripPathSitePrefix($this->absoluteBasePath), '/');
                 if ($temporaryBaseUri !== '') {
@@ -140,8 +142,6 @@ class LocalDriver extends AbstractHierarchicalFilesystemDriver implements Stream
                     $temporaryBaseUri = implode('/', $uriParts) . '/';
                 }
                 $this->baseUri = $temporaryBaseUri;
-            } elseif (isset($this->configuration['baseUri']) && GeneralUtility::isValidUrl($this->configuration['baseUri'])) {
-                $this->baseUri = rtrim($this->configuration['baseUri'], '/') . '/';
             }
         }
     }
@@ -186,7 +186,6 @@ class LocalDriver extends AbstractHierarchicalFilesystemDriver implements Stream
      *
      * @param string $identifier
      * @return string|null NULL if file is missing or deleted, the generated url otherwise
-     * @throws \TYPO3\CMS\Core\Resource\Exception
      */
     public function getPublicUrl($identifier)
     {
diff --git a/typo3/sysext/core/Configuration/Resource/Driver/LocalDriverFlexForm.xml b/typo3/sysext/core/Configuration/Resource/Driver/LocalDriverFlexForm.xml
index d14d8da65a36cd12f10e3ffeda50694026b4cb5f..3dca8eb702469aedda69e53bcf160c99492f2702 100644
--- a/typo3/sysext/core/Configuration/Resource/Driver/LocalDriverFlexForm.xml
+++ b/typo3/sysext/core/Configuration/Resource/Driver/LocalDriverFlexForm.xml
@@ -33,6 +33,16 @@
 					</config>
 				</TCEforms>
 			</pathType>
+			<baseUri>
+				<TCEforms>
+					<label>LLL:EXT:core/Resources/Private/Language/locallang_mod_file.xlf:sys_file_storage.localDriverFlexform_baseUri</label>
+					<config>
+						<type>input</type>
+						<placeholder>LLL:EXT:core/Resources/Private/Language/locallang_mod_file.xlf:sys_file_storage.localDriverFlexform_baseUri_placeholder</placeholder>
+						<size>30</size>
+					</config>
+				</TCEforms>
+			</baseUri>
 			<caseSensitive>
 				<TCEforms>
 					<label>LLL:EXT:core/Resources/Private/Language/locallang_mod_file.xlf:sys_file_storage.localDriverFlexform_caseSensitive</label>
diff --git a/typo3/sysext/core/Resources/Private/Language/locallang_mod_file.xlf b/typo3/sysext/core/Resources/Private/Language/locallang_mod_file.xlf
index c232af1fc3daa7197c630f7273c5ad489a91cfce..ec84426eab961dcd0ffd2ec1aa7f61ece9582443 100644
--- a/typo3/sysext/core/Resources/Private/Language/locallang_mod_file.xlf
+++ b/typo3/sysext/core/Resources/Private/Language/locallang_mod_file.xlf
@@ -18,6 +18,12 @@
 			<trans-unit id="sys_file_storage.localDriverFlexform_basePath_placeholder" resname="sys_file_storage.localDriverFlexform_basePath_placeholder">
 				<source>Add storage path here. For example: myPath/folder/</source>
 			</trans-unit>
+			<trans-unit id="sys_file_storage.localDriverFlexform_baseUri" resname="sys_file_storage.localDriverFlexform_baseUri">
+				<source>Base URI</source>
+			</trans-unit>
+			<trans-unit id="sys_file_storage.localDriverFlexform_baseUri_placeholder" resname="sys_file_storage.localDriverFlexform_baseUri_placeholder">
+				<source>Use this URI to build the public url of files, instead of deriving the URI from base path</source>
+			</trans-unit>
 			<trans-unit id="sys_file_storage.localDriverFlexform_pathType" resname="sys_file_storage.localDriverFlexform_pathType">
 				<source>Path type</source>
 			</trans-unit>
diff --git a/typo3/sysext/core/Tests/Unit/Resource/Driver/LocalDriverTest.php b/typo3/sysext/core/Tests/Unit/Resource/Driver/LocalDriverTest.php
index 5b7ae29a3ff5d2c2d08ce0fd8353d2e91ad40573..4ea00e03b52a7548d6b15ef9b790c26cea660694 100644
--- a/typo3/sysext/core/Tests/Unit/Resource/Driver/LocalDriverTest.php
+++ b/typo3/sysext/core/Tests/Unit/Resource/Driver/LocalDriverTest.php
@@ -26,6 +26,7 @@ use TYPO3\CMS\Core\Resource\Exception\ExistingTargetFileNameException;
 use TYPO3\CMS\Core\Resource\Exception\FileOperationErrorException;
 use TYPO3\CMS\Core\Resource\Exception\InvalidFileNameException;
 use TYPO3\CMS\Core\Resource\ResourceStorage;
+use TYPO3\CMS\Core\Resource\ResourceStorageInterface;
 use TYPO3\CMS\Core\Tests\Unit\Resource\BaseTestCase;
 use TYPO3\CMS\Core\Tests\Unit\Resource\Driver\Fixtures\LocalDriverFilenameFilter;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
@@ -41,6 +42,11 @@ class LocalDriverTest extends BaseTestCase
      */
     protected $resetSingletonInstances = true;
 
+    /**
+     * @var bool Reset changed Environment
+     */
+    protected $backupEnvironment = true;
+
     /**
      * @var LocalDriver
      */
@@ -181,6 +187,103 @@ class LocalDriverTest extends BaseTestCase
         self::assertStringNotContainsString('/../', $basePath);
     }
 
+    public function publicUrlIsCalculatedCorrectlyWithDifferentBasePathsAndBasUrisDataProvider(): array
+    {
+        return [
+            'no base uri, within public' => [
+                '/files/',
+                '',
+                '/foo.txt',
+                true,
+                'files/foo.txt',
+            ],
+            'no base uri, within project' => [
+                '/../files/',
+                '',
+                '/foo.txt',
+                false,
+                null,
+            ],
+            'base uri with host, within public' => [
+                '/files/',
+                'https://host.tld/',
+                '/foo.txt',
+                true,
+                'https://host.tld/foo.txt',
+            ],
+            'base uri with host, within project' => [
+                '/../files/',
+                'https://host.tld/',
+                '/foo.txt',
+                true,
+                'https://host.tld/foo.txt',
+            ],
+            'base uri with path only, within public' => [
+                '/files/',
+                'assets/',
+                '/foo.txt',
+                true,
+                'assets/foo.txt',
+            ],
+            'base uri with path only, within project' => [
+                '/../files/',
+                'assets/',
+                '/foo.txt',
+                true,
+                'assets/foo.txt',
+            ],
+            'base uri with path only, within other public dir' => [
+                '/../public/assets/',
+                'assets/',
+                '/foo.txt',
+                true,
+                'assets/foo.txt',
+            ],
+        ];
+    }
+
+    /**
+     * @param string $basePath
+     * @param string $baseUri
+     * @param string $fileName
+     * @param bool $expectedIsPublic
+     * @param string|null $expectedPublicUrl
+     * @test
+     * @dataProvider publicUrlIsCalculatedCorrectlyWithDifferentBasePathsAndBasUrisDataProvider
+     */
+    public function publicUrlIsCalculatedCorrectlyWithDifferentBasePathsAndBasUris(string $basePath, string $baseUri, string $fileName, bool $expectedIsPublic, ?string $expectedPublicUrl): void
+    {
+        $testDir = $this->createRealTestdir();
+        $projectPath = $testDir . '/app';
+        $publicPath = $projectPath . '/public';
+        $absoluteBaseDir = $publicPath . $basePath;
+        mkdir($projectPath);
+        mkdir($publicPath);
+        mkdir($absoluteBaseDir, 0777, true);
+        Environment::initialize(
+            Environment::getContext(),
+            true,
+            false,
+            $projectPath,
+            $publicPath,
+            Environment::getVarPath(),
+            Environment::getConfigPath(),
+            Environment::getCurrentScript(),
+            Environment::isUnix() ? 'UNIX' : 'WINDOWS'
+        );
+        $driverConfiguration = [
+            'pathType' => 'relative',
+            'basePath' => $basePath,
+            'baseUri' => $baseUri,
+        ];
+
+        $subject = $this->createDriver($driverConfiguration);
+
+        self::assertSame($expectedIsPublic, $subject->hasCapability(ResourceStorageInterface::CAPABILITY_PUBLIC));
+        self::assertSame($fileName, $subject->createFile($fileName, '/'));
+        self::assertSame($expectedPublicUrl, $subject->getPublicUrl($fileName));
+    }
+
     /**
      * @test
      */