diff --git a/core/core.services.yml b/core/core.services.yml
index 2557821b85ab32910242b716d542c41313452ea3..6ac179594bc7c46aba503f6fa2fef3ed5ffb71b0 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -708,7 +708,7 @@ services:
     class: Drupal\Core\Menu\StaticMenuLinkOverrides
     arguments: ['@config.factory']
   request_stack:
-    class: Symfony\Component\HttpFoundation\RequestStack
+    class: Drupal\Core\Http\RequestStack
     tags:
       - { name: persist }
   current_route_match:
diff --git a/core/lib/Drupal/Core/DrupalKernel.php b/core/lib/Drupal/Core/DrupalKernel.php
index 3eee8d871ce64053139db7bdd1c7a96450019de7..7e2cbe13a1f1789db84bc47ba8bb37ecd21f34f9 100644
--- a/core/lib/Drupal/Core/DrupalKernel.php
+++ b/core/lib/Drupal/Core/DrupalKernel.php
@@ -937,9 +937,9 @@ protected function initializeContainer() {
     }
 
     // The request stack is preserved across container rebuilds. Reinject the
-    // new session into the master request if one was present before.
+    // new session into the main request if one was present before.
     if (($request_stack = $this->container->get('request_stack', ContainerInterface::NULL_ON_INVALID_REFERENCE))) {
-      if ($request = $request_stack->getMasterRequest()) {
+      if ($request = $request_stack->getMainRequest()) {
         $subrequest = TRUE;
         if ($request->hasSession()) {
           $request->setSession($this->container->get('session'));
diff --git a/core/lib/Drupal/Core/Form/FormBuilder.php b/core/lib/Drupal/Core/Form/FormBuilder.php
index 1d92afc45d8fad4a2e2e965a471934d9a63fe172..e4e8071923a9207d5fd792696d4f70c9693781ac 100644
--- a/core/lib/Drupal/Core/Form/FormBuilder.php
+++ b/core/lib/Drupal/Core/Form/FormBuilder.php
@@ -844,9 +844,9 @@ public function prepareForm($form_id, &$form, FormStateInterface &$form_state) {
    *   The URL to be used as the $form['#action'].
    */
   protected function buildFormAction() {
-    // @todo Use <current> instead of the master request in
+    // @todo Use <current> instead of the main request in
     //   https://www.drupal.org/node/2505339.
-    $request = $this->requestStack->getMasterRequest();
+    $request = $this->requestStack->getMainRequest();
     $request_uri = $request->getRequestUri();
 
     // Prevent cross site requests via the Form API by using an absolute URL
diff --git a/core/lib/Drupal/Core/Http/RequestStack.php b/core/lib/Drupal/Core/Http/RequestStack.php
new file mode 100644
index 0000000000000000000000000000000000000000..328550069a08b188a6fa8b2be0f2849f1cce1171
--- /dev/null
+++ b/core/lib/Drupal/Core/Http/RequestStack.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace Drupal\Core\Http;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\RequestStack as SymfonyRequestStack;
+
+/**
+ * Forward-compatibility shim for Symfony's RequestStack.
+ *
+ * @todo Remove when Symfony 5.3 or greater is required.
+ */
+class RequestStack extends SymfonyRequestStack {
+
+  /**
+   * Gets the main request.
+   *
+   * @return \Symfony\Component\HttpFoundation\Request|null
+   *   The main request.
+   */
+  public function getMainRequest(): ?Request {
+    if (method_exists(SymfonyRequestStack::class, 'getMainRequest')) {
+      return parent::getMainRequest();
+    }
+    else {
+      return parent::getMasterRequest();
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getMasterRequest() {
+    @trigger_error('Drupal\Core\Http\RequestStack::getMasterRequest() is deprecated, use getMainRequest() instead.', E_USER_DEPRECATED);
+    return $this->getMainRequest();
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Render/RenderCache.php b/core/lib/Drupal/Core/Render/RenderCache.php
index 20e4b0c142bc6e6b02d73beb7ff3ed0330e049e6..1a0233488947cfb364b942e5d4c08dae46037b86 100644
--- a/core/lib/Drupal/Core/Render/RenderCache.php
+++ b/core/lib/Drupal/Core/Render/RenderCache.php
@@ -281,7 +281,7 @@ public function set(array &$elements, array $pre_bubbling_elements) {
    * @see \Drupal\Core\Cache\CacheBackendInterface::set()
    */
   protected function maxAgeToExpire($max_age) {
-    return ($max_age === Cache::PERMANENT) ? Cache::PERMANENT : (int) $this->requestStack->getMasterRequest()->server->get('REQUEST_TIME') + $max_age;
+    return ($max_age === Cache::PERMANENT) ? Cache::PERMANENT : (int) $this->requestStack->getMainRequest()->server->get('REQUEST_TIME') + $max_age;
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Routing/CurrentRouteMatch.php b/core/lib/Drupal/Core/Routing/CurrentRouteMatch.php
index 195a48d574b859340283c94dc1610c50d6bca723..0bec8930bab43a816884f2bf0e8fa3382ad2e75e 100644
--- a/core/lib/Drupal/Core/Routing/CurrentRouteMatch.php
+++ b/core/lib/Drupal/Core/Routing/CurrentRouteMatch.php
@@ -124,7 +124,7 @@ public function resetRouteMatch() {
    * {@inheritdoc}
    */
   public function getMasterRouteMatch() {
-    return $this->getRouteMatch($this->requestStack->getMasterRequest());
+    return $this->getRouteMatch($this->requestStack->getMainRequest());
   }
 
   /**
diff --git a/core/lib/Drupal/Core/TempStore/PrivateTempStore.php b/core/lib/Drupal/Core/TempStore/PrivateTempStore.php
index 66966715343a7259606a26fe6b2e7ad5dbe97fa5..17f25c874cb077579f28073dc68280b4d4a56db4 100644
--- a/core/lib/Drupal/Core/TempStore/PrivateTempStore.php
+++ b/core/lib/Drupal/Core/TempStore/PrivateTempStore.php
@@ -143,7 +143,7 @@ public function set($key, $value) {
     $value = (object) [
       'owner' => $this->getOwner(),
       'data' => $value,
-      'updated' => (int) $this->requestStack->getMasterRequest()->server->get('REQUEST_TIME'),
+      'updated' => (int) $this->requestStack->getMainRequest()->server->get('REQUEST_TIME'),
     ];
     $this->storage->setWithExpire($key, $value, $this->expire);
     $this->lockBackend->release($key);
diff --git a/core/lib/Drupal/Core/TempStore/SharedTempStore.php b/core/lib/Drupal/Core/TempStore/SharedTempStore.php
index f26145a225dd39ebe15f9ef1cf17bc8b9b35f74d..75ec79a3ae392b791358c17b93d108f1547d160f 100644
--- a/core/lib/Drupal/Core/TempStore/SharedTempStore.php
+++ b/core/lib/Drupal/Core/TempStore/SharedTempStore.php
@@ -168,7 +168,7 @@ public function setIfNotExists($key, $value) {
     $value = (object) [
       'owner' => $this->owner,
       'data' => $value,
-      'updated' => (int) $this->requestStack->getMasterRequest()->server->get('REQUEST_TIME'),
+      'updated' => (int) $this->requestStack->getMainRequest()->server->get('REQUEST_TIME'),
     ];
     $this->ensureAnonymousSession();
     $set = $this->storage->setWithExpireIfNotExists($key, $value, $this->expire);
@@ -228,7 +228,7 @@ public function set($key, $value) {
     $value = (object) [
       'owner' => $this->owner,
       'data' => $value,
-      'updated' => (int) $this->requestStack->getMasterRequest()->server->get('REQUEST_TIME'),
+      'updated' => (int) $this->requestStack->getMainRequest()->server->get('REQUEST_TIME'),
     ];
     $this->ensureAnonymousSession();
     $this->storage->setWithExpire($key, $value, $this->expire);
diff --git a/core/modules/big_pipe/src/Render/BigPipe.php b/core/modules/big_pipe/src/Render/BigPipe.php
index e6763f898a58c70e93e183e45201aaad6298a123..3067673e76237634aa01fbd97adcddb84ae5d154 100644
--- a/core/modules/big_pipe/src/Render/BigPipe.php
+++ b/core/modules/big_pipe/src/Render/BigPipe.php
@@ -361,7 +361,7 @@ protected function sendPreBody($pre_body, array $no_js_placeholders, AttachedAss
       // KernelEvents::RESPONSE event. This results in the attachments for the
       // HTML response being processed by HtmlResponseAttachmentsProcessor and
       // hence the HTML to load the bottom JavaScript can be rendered.
-      $fake_request = $this->requestStack->getMasterRequest()->duplicate();
+      $fake_request = $this->requestStack->getMainRequest()->duplicate();
       $html_response = $this->filterEmbeddedResponse($fake_request, $html_response);
       $scripts_bottom = $html_response->getContent();
     }
@@ -464,7 +464,7 @@ protected function sendNoJsPlaceholders($html, $no_js_placeholders, AttachedAsse
       // hence:
       // - the HTML to load the CSS can be rendered.
       // - the HTML to load the JS (at the top) can be rendered.
-      $fake_request = $this->requestStack->getMasterRequest()->duplicate();
+      $fake_request = $this->requestStack->getMainRequest()->duplicate();
       $fake_request->request->set('ajax_page_state', ['libraries' => implode(',', $cumulative_assets->getAlreadyLoadedLibraries())]);
       try {
         $html_response = $this->filterEmbeddedResponse($fake_request, $html_response);
@@ -528,12 +528,12 @@ protected function sendPlaceholders(array $placeholders, array $placeholder_orde
 
     // A BigPipe response consists of an HTML response plus multiple embedded
     // AJAX responses. To process the attachments of those AJAX responses, we
-    // need a fake request that is identical to the master request, but with
+    // need a fake request that is identical to the main request, but with
     // one change: it must have the right Accept header, otherwise the work-
     // around for a bug in IE9 will cause not JSON, but <textarea>-wrapped JSON
     // to be returned.
     // @see \Drupal\Core\EventSubscriber\AjaxResponseSubscriber::onResponse()
-    $fake_request = $this->requestStack->getMasterRequest()->duplicate();
+    $fake_request = $this->requestStack->getMainRequest()->duplicate();
     $fake_request->headers->set('Accept', 'application/vnd.drupal-ajax');
 
     foreach ($placeholder_order as $placeholder_id) {
diff --git a/core/modules/big_pipe/tests/modules/big_pipe_test/src/BigPipeTestController.php b/core/modules/big_pipe/tests/modules/big_pipe_test/src/BigPipeTestController.php
index 5bb3505500bb219d2ecc989f624f14983fd30870..0b36f1f8b26819947a2a8d8ed815212871f753b7 100644
--- a/core/modules/big_pipe/tests/modules/big_pipe_test/src/BigPipeTestController.php
+++ b/core/modules/big_pipe/tests/modules/big_pipe_test/src/BigPipeTestController.php
@@ -14,7 +14,7 @@ class BigPipeTestController implements TrustedCallbackInterface {
    * @return array
    */
   public function test() {
-    $has_session = \Drupal::service('session_configuration')->hasSession(\Drupal::requestStack()->getMasterRequest());
+    $has_session = \Drupal::service('session_configuration')->hasSession(\Drupal::requestStack()->getMainRequest());
 
     $build = [];
 
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 6a3ac6b6463fcde559aa2d1390784b5bbb88605f..bbad086730171e3c714b7851c20b20521852fa73 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -700,9 +700,8 @@ function system_js_settings_build(&$settings, AttachedAssetsInterface $assets) {
  * as well as theme_token ajax state.
  */
 function system_js_settings_alter(&$settings, AttachedAssetsInterface $assets) {
-  // As this is being output in the final response always use the master
-  // request.
-  $request = \Drupal::requestStack()->getMasterRequest();
+  // As this is being output in the final response always use the main request.
+  $request = \Drupal::requestStack()->getMainRequest();
   $current_query = $request->query->all();
 
   // Let output path processors set a prefix.
diff --git a/core/tests/Drupal/Tests/Core/Http/RequestStackLegacyTest.php b/core/tests/Drupal/Tests/Core/Http/RequestStackLegacyTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..5860a12bab7868a9b37b45602ea1c3fc0c0a0532
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Http/RequestStackLegacyTest.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace Drupal\Tests\Core\Http;
+
+use Drupal\Core\Http\RequestStack;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * @coversDefaultClass \Drupal\Core\Http\RequestStack
+ * @group legacy
+ */
+class RequestStackLegacyTest extends UnitTestCase {
+
+  /**
+   * Tests deprecation message in our subclassed RequestStack.
+   *
+   * @covers ::getMasterRequest
+   */
+  public function testGetMasterRequestDeprecation() {
+    $stack = new RequestStack();
+
+    $this->expectDeprecation('Drupal\Core\Http\RequestStack::getMasterRequest() is deprecated, use getMainRequest() instead.');
+    $this->assertNull($stack->getMasterRequest());
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/Core/Render/RendererTestBase.php b/core/tests/Drupal/Tests/Core/Render/RendererTestBase.php
index a788ec7ebe089e2241c6366c24d6e4558a77991a..198f29631738fcf91853ba47d65f1911d38e4f75 100644
--- a/core/tests/Drupal/Tests/Core/Render/RendererTestBase.php
+++ b/core/tests/Drupal/Tests/Core/Render/RendererTestBase.php
@@ -11,6 +11,7 @@
 use Drupal\Core\Cache\CacheableMetadata;
 use Drupal\Core\Cache\Context\ContextCacheKeys;
 use Drupal\Core\Cache\MemoryBackend;
+use Drupal\Core\Http\RequestStack;
 use Drupal\Core\Security\TrustedCallbackInterface;
 use Drupal\Core\Render\PlaceholderGenerator;
 use Drupal\Core\Render\PlaceholderingRenderCache;
@@ -18,7 +19,6 @@
 use Drupal\Tests\UnitTestCase;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\HttpFoundation\RequestStack;
 
 /**
  * Base class for the actual unit tests testing \Drupal\Core\Render\Renderer.
diff --git a/core/tests/Drupal/Tests/Core/TempStore/PrivateTempStoreTest.php b/core/tests/Drupal/Tests/Core/TempStore/PrivateTempStoreTest.php
index 40e17660177c544ef5c6f8cbe8d86ba64f1f187e..90cf073dd093970f6804f2ce68a3621954fc4035 100644
--- a/core/tests/Drupal/Tests/Core/TempStore/PrivateTempStoreTest.php
+++ b/core/tests/Drupal/Tests/Core/TempStore/PrivateTempStoreTest.php
@@ -2,12 +2,12 @@
 
 namespace Drupal\Tests\Core\TempStore;
 
+use Drupal\Core\Http\RequestStack;
 use Drupal\Core\TempStore\Lock;
 use Drupal\Tests\UnitTestCase;
 use Drupal\Core\TempStore\PrivateTempStore;
 use Drupal\Core\TempStore\TempStoreException;
 use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\HttpFoundation\RequestStack;
 
 /**
  * @coversDefaultClass \Drupal\Core\TempStore\PrivateTempStore
diff --git a/core/tests/Drupal/Tests/Core/TempStore/SharedTempStoreTest.php b/core/tests/Drupal/Tests/Core/TempStore/SharedTempStoreTest.php
index 585fc47ac2aa73d5979ba33a5bba9f62f0f9b074..935c5fac8c4dc3f800a34f3c8acdc431eb82a633 100644
--- a/core/tests/Drupal/Tests/Core/TempStore/SharedTempStoreTest.php
+++ b/core/tests/Drupal/Tests/Core/TempStore/SharedTempStoreTest.php
@@ -3,6 +3,7 @@
 namespace Drupal\Tests\Core\TempStore;
 
 use Drupal\Core\DependencyInjection\ContainerBuilder;
+use Drupal\Core\Http\RequestStack;
 use Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface;
 use Drupal\Core\Session\AccountProxyInterface;
 use Drupal\Core\TempStore\Lock;
@@ -12,7 +13,6 @@
 use Drupal\Core\TempStore\TempStoreException;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\HttpFoundation\RequestStack;
 use Symfony\Component\HttpFoundation\Session\SessionInterface;
 
 /**