diff --git a/core/modules/simpletest/src/WebTestBase.php b/core/modules/simpletest/src/WebTestBase.php
index 6ff76aee61..90d619eaf5 100644
--- a/core/modules/simpletest/src/WebTestBase.php
+++ b/core/modules/simpletest/src/WebTestBase.php
@@ -21,6 +21,7 @@
 use Drupal\system\Tests\Cache\AssertPageCacheContextsAndTagsTrait;
 use Drupal\Tests\Traits\Core\CronRunTrait;
 use Drupal\Tests\TestFileCreationTrait;
+use Drupal\Tests\Traits\Core\MetaRefreshTrait;
 use Drupal\Tests\XdebugRequestTrait;
 use Zend\Diactoros\Uri;
 
@@ -58,6 +59,7 @@
     createAdminRole as drupalCreateAdminRole;
   }
 
+  use MetaRefreshTrait;
   use XdebugRequestTrait;
 
   /**
@@ -171,21 +173,6 @@
    */
   protected $redirectCount;
 
-
-  /**
-   * The number of meta refresh redirects to follow, or NULL if unlimited.
-   *
-   * @var null|int
-   */
-  protected $maximumMetaRefreshCount = NULL;
-
-  /**
-   * The number of meta refresh redirects followed during ::drupalGet().
-   *
-   * @var int
-   */
-  protected $metaRefreshCount = 0;
-
   /**
    * Cookies to set on curl requests.
    *
@@ -626,7 +613,7 @@ protected function tearDown() {
     parent::tearDown();
 
     // Ensure that the maximum meta refresh count is reset.
-    $this->maximumMetaRefreshCount = NULL;
+    $this->resetMetaRefreshCounters();
 
     // Ensure that internal logged in variable and cURL options are reset.
     $this->loggedInUser = FALSE;
@@ -1567,30 +1554,6 @@ protected function translatePostValues(array $values) {
   }
 
   /**
-   * Checks for meta refresh tag and if found call drupalGet() recursively.
-   *
-   * This function looks for the http-equiv attribute to be set to "Refresh" and
-   * is case-sensitive.
-   *
-   * @return
-   *   Either the new page content or FALSE.
-   */
-  protected function checkForMetaRefresh() {
-    if (strpos($this->getRawContent(), '<meta ') && $this->parse() && (!isset($this->maximumMetaRefreshCount) || $this->metaRefreshCount < $this->maximumMetaRefreshCount)) {
-      $refresh = $this->xpath('//meta[@http-equiv="Refresh"]');
-      if (!empty($refresh)) {
-        // Parse the content attribute of the meta tag for the format:
-        // "[delay]: URL=[page_to_redirect_to]".
-        if (preg_match('/\d+;\s*URL=(?<url>.*)/i', $refresh[0]['content'], $match)) {
-          $this->metaRefreshCount++;
-          return $this->drupalGet($this->getAbsoluteUrl(Html::decodeEntities($match['url'])));
-        }
-      }
-    }
-    return FALSE;
-  }
-
-  /**
    * Retrieves only the headers for a Drupal path or an absolute path.
    *
    * @param $path
diff --git a/core/tests/Drupal/Tests/Traits/Core/MetaRefreshTrait.php b/core/tests/Drupal/Tests/Traits/Core/MetaRefreshTrait.php
new file mode 100644
index 0000000000..0d17af3655
--- /dev/null
+++ b/core/tests/Drupal/Tests/Traits/Core/MetaRefreshTrait.php
@@ -0,0 +1,85 @@
+<?php
+
+namespace Drupal\Tests\Traits\Core;
+
+use Behat\Mink\Element\NodeElement;
+use Drupal\Component\Utility\Html;
+
+/**
+ * Allows tests redirect on meta refresh tag found in page.
+ */
+trait MetaRefreshTrait {
+
+  /**
+   * The number of meta refresh redirects to follow, or NULL if unlimited.
+   *
+   * @var null|int
+   */
+  protected $maximumMetaRefreshCount = NULL;
+
+  /**
+   * The number of meta refresh redirects followed during ::drupalGet().
+   *
+   * @var int
+   */
+  protected $metaRefreshCount = 0;
+
+  /**
+   * Checks for meta refresh tag and if found call drupalGet() recursively.
+   *
+   * This function looks for the http-equiv attribute to be set to "Refresh" and
+   * is case-sensitive.
+   *
+   * @return string|false
+   *   Either the new page content or FALSE.
+   */
+  protected function checkForMetaRefresh() {
+    $refresh = $this->cssSelect('meta[http-equiv="Refresh"]');
+    if (!empty($refresh) && (!isset($this->maximumMetaRefreshCount) || $this->metaRefreshCount < $this->maximumMetaRefreshCount)) {
+      $content = $this->getElementValueByAttribute($refresh[0], 'content');
+      // Parse the content attribute of the meta tag for the format:
+      // "[delay]: URL=[page_to_redirect_to]".
+      if (preg_match('/\d+;\s*URL=(?<url>.*)/i', $content, $match)) {
+        $this->metaRefreshCount++;
+        return $this->drupalGet($this->getAbsoluteUrl(Html::decodeEntities($match['url'])));
+      }
+    }
+    return FALSE;
+  }
+
+  /**
+   * Resets the meta refresh counters.
+   */
+  protected function resetMetaRefreshCounters() {
+    $this->maximumMetaRefreshCount = NULL;
+    $this->metaRefreshCount = 0;
+  }
+
+  /**
+   * Gets the value of a markup attribute given an element.
+   *
+   * @param \SimpleXMLElement|\Behat\Mink\Element\NodeElement $element
+   *   The element from which to extract the attribute value. It can be either a
+   *   \SimpleXMLElement element (as WebTestBase::xpath returns) or a
+   *   NodeElement (a BrowserTestBase::xpath() return value).
+   * @param string $attribute
+   *   The attribute for which to return the value.
+   *
+   * @return string
+   *
+   * @throws \InvalidArgumentException
+   *   When the passed element is neither SimpleXMLElement, nor NodeElement.
+   *
+   * @deprecated Scheduled for removal before Drupal 9.0.0.
+   */
+  protected function getElementValueByAttribute($element, $attribute) {
+    if ($element instanceof \SimpleXMLElement) {
+      return isset($element[$attribute]) ? (string) $element[$attribute] : NULL;
+    }
+    elseif($element instanceof NodeElement) {
+      return $element->getAttribute($attribute);
+    }
+    throw new \InvalidArgumentException('Element should be either \\SimpleXMLElement or \\Behat\\Mink\\Element\\NodeElement');
+  }
+
+}
