diff --git a/core/modules/views/src/Plugin/views/field/FieldPluginBase.php b/core/modules/views/src/Plugin/views/field/FieldPluginBase.php
index 5eb5765..60fece1 100644
--- a/core/modules/views/src/Plugin/views/field/FieldPluginBase.php
+++ b/core/modules/views/src/Plugin/views/field/FieldPluginBase.php
@@ -1342,7 +1342,7 @@ protected function renderAsLink($alter, $text, $tokens) {
     }
 
     // Parse the URL and move any query and fragment parameters out of the path.
-    $url = parse_url($path);
+    $url = UrlHelper::parse($path);
 
     // Seriously malformed URLs may return FALSE or empty arrays.
     if (empty($url)) {
@@ -1356,6 +1356,10 @@ protected function renderAsLink($alter, $text, $tokens) {
       return $text;
     }
 
+    // If we get to here we have a path from the url parsing. So assign that to
+    // $path now so we don't get query strings or fragments in the path.
+    $path = $url['path'];
+
     // If no scheme is provided in the $path, assign the default 'http://'.
     // This allows a url of 'www.example.com' to be converted to 'http://www.example.com'.
     // Only do this on for external URLs.
@@ -1364,23 +1368,28 @@ protected function renderAsLink($alter, $text, $tokens) {
         // There is no scheme, add the default 'http://' to the $path.
         $path = "http://$path";
         // Reset the $url array to include the new scheme.
-        $url = parse_url($path);
+        $url = UrlHelper::parse($path);
       }
     }
 
     if (isset($url['query'])) {
-      $path = strtr($path, array('?' . $url['query'] => ''));
-      $query = array();
-      parse_str($url['query'], $query);
       // Remove query parameters that were assigned a query string replacement
       // token for which there is no value available.
-      foreach ($query as $param => $val) {
+      foreach ($url['query'] as $param => $val) {
         if ($val == '%' . $param) {
-          unset($query[$param]);
+          unset($url['query'][$param]);
+        }
+        // Replace any empty query params from URL parsing with NULL. So the
+        // query will get built correctly with only the param key.
+        // @see \Drupal\Component\Utility\UrlHelper::buildQuery().
+        if ($val === '') {
+          $url['query'][$param] = NULL;
         }
       }
-      $options['query'] = $query;
+
+      $options['query'] = $url['query'];
     }
+
     if (isset($url['fragment'])) {
       $path = strtr($path, array('#' . $url['fragment'] => ''));
       // If the path is empty we want to have a fragment for the current site.
diff --git a/core/modules/views/src/Tests/Handler/FieldWebTest.php b/core/modules/views/src/Tests/Handler/FieldWebTest.php
index d4b1781..1c7c6e5 100644
--- a/core/modules/views/src/Tests/Handler/FieldWebTest.php
+++ b/core/modules/views/src/Tests/Handler/FieldWebTest.php
@@ -235,8 +235,7 @@ public function testAlterUrl() {
       $expected_result = url('node/123', array('query' => array('foo' => NULL), 'fragment' => 'bar', 'absolute' => $absolute));
       $alter['path'] = 'node/123?foo#bar';
       $result = $id_field->theme($row);
-      // @fixme: The actual result is node/123?foo#bar so views has a bug here.
-      // $this->assertSubStringExists(decode_entities($result), decode_entities($expected_result));
+      $this->assertSubString(decode_entities($result), decode_entities($expected_result));
 
       $expected_result = url('<front>', array('absolute' => $absolute));
       $alter['path'] = '<front>';
