diff --git a/core/modules/link/src/Plugin/Field/FieldFormatter/LinkFormatter.php b/core/modules/link/src/Plugin/Field/FieldFormatter/LinkFormatter.php
index 4ea672d273696db7e6c0b182b1710c0c00a9305a..7d2d0eeabd3c51b7054e7eab76d3793e2c2d56e1 100644
--- a/core/modules/link/src/Plugin/Field/FieldFormatter/LinkFormatter.php
+++ b/core/modules/link/src/Plugin/Field/FieldFormatter/LinkFormatter.php
@@ -242,6 +242,7 @@ protected function buildUrl(LinkItemInterface $item) {
 
     $settings = $this->getSettings();
     $options = $item->options;
+    $options += $url->getOptions();
 
     // Add optional 'rel' attribute to link options.
     if (!empty($settings['rel'])) {
diff --git a/core/modules/link/src/Tests/LinkFieldTest.php b/core/modules/link/src/Tests/LinkFieldTest.php
index 05148d96de7cf16aba9934b665fd9b2990fca637..477382bcc3d8a810e213032f2c18753e4b25d55c 100644
--- a/core/modules/link/src/Tests/LinkFieldTest.php
+++ b/core/modules/link/src/Tests/LinkFieldTest.php
@@ -344,7 +344,7 @@ function testLinkFormatter() {
       'field_name' => $field_name,
       'entity_type' => 'entity_test',
       'type' => 'link',
-      'cardinality' => 2,
+      'cardinality' => 3,
     ));
     $this->fieldStorage->save();
     FieldConfig::create([
@@ -369,23 +369,28 @@ function testLinkFormatter() {
       ->setComponent($field_name, $display_options)
       ->save();
 
-    // Create an entity with two link field values:
+    // Create an entity with three link field values:
     // - The first field item uses a URL only.
     // - The second field item uses a URL and link text.
+    // - The third field item uses a fragment-only URL with text.
     // For consistency in assertion code below, the URL is assigned to the title
     // variable for the first field.
     $this->drupalGet('entity_test/add');
     $url1 = 'http://www.example.com/content/articles/archive?author=John&year=2012#com';
     $url2 = 'http://www.example.org/content/articles/archive?author=John&year=2012#org';
+    $url3 = '#net';
     $title1 = $url1;
     // Intentionally contains an ampersand that needs sanitization on output.
     $title2 = 'A very long & strange example title that could break the nice layout of the site';
+    $title3 = 'Fragment only';
     $edit = array(
       "{$field_name}[0][uri]" => $url1,
       // Note that $title1 is not submitted.
       "{$field_name}[0][title]" => '',
       "{$field_name}[1][uri]" => $url2,
       "{$field_name}[1][title]" => $title2,
+      "{$field_name}[2][uri]" => $url3,
+      "{$field_name}[2][title]" => $title3,
     );
     // Assert label is shown.
     $this->assertText('Read more about this entity');
@@ -433,18 +438,24 @@ function testLinkFormatter() {
             $url = $url2;
             $title = isset($new_value) ? Unicode::truncate($title2, $new_value, FALSE, TRUE) : $title2;
             $this->assertRaw('<a href="' . Html::escape($url) . '">' . Html::escape($title) . '</a>');
+
+            $url = $url3;
+            $title = isset($new_value) ? Unicode::truncate($title3, $new_value, FALSE, TRUE) : $title3;
+            $this->assertRaw('<a href="' . Html::escape($url) . '">' . Html::escape($title) . '</a>');
             break;
 
           case 'rel':
             $rel = isset($new_value) ? ' rel="' . $new_value . '"' : '';
             $this->assertRaw('<a href="' . Html::escape($url1) . '"' . $rel . '>' . Html::escape($title1) . '</a>');
             $this->assertRaw('<a href="' . Html::escape($url2) . '"' . $rel . '>' . Html::escape($title2) . '</a>');
+            $this->assertRaw('<a href="' . Html::escape($url3) . '"' . $rel . '>' . Html::escape($title3) . '</a>');
             break;
 
           case 'target':
             $target = isset($new_value) ? ' target="' . $new_value . '"' : '';
             $this->assertRaw('<a href="' . Html::escape($url1) . '"' . $target . '>' . Html::escape($title1) . '</a>');
             $this->assertRaw('<a href="' . Html::escape($url2) . '"' . $target . '>' . Html::escape($title2) . '</a>');
+            $this->assertRaw('<a href="' . Html::escape($url3) . '"' . $target . '>' . Html::escape($title3) . '</a>');
             break;
 
           case 'url_only':
@@ -452,17 +463,21 @@ function testLinkFormatter() {
             if (!$new_value['url_only']) {
               $this->assertRaw('<a href="' . Html::escape($url1) . '">' . Html::escape($title1) . '</a>');
               $this->assertRaw('<a href="' . Html::escape($url2) . '">' . Html::escape($title2) . '</a>');
+              $this->assertRaw('<a href="' . Html::escape($url3) . '">' . Html::escape($title3) . '</a>');
             }
             else {
               if (empty($new_value['url_plain'])) {
                 $this->assertRaw('<a href="' . Html::escape($url1) . '">' . Html::escape($url1) . '</a>');
                 $this->assertRaw('<a href="' . Html::escape($url2) . '">' . Html::escape($url2) . '</a>');
+                $this->assertRaw('<a href="' . Html::escape($url3) . '">' . Html::escape($url3) . '</a>');
               }
               else {
                 $this->assertNoRaw('<a href="' . Html::escape($url1) . '">' . Html::escape($url1) . '</a>');
                 $this->assertNoRaw('<a href="' . Html::escape($url2) . '">' . Html::escape($url2) . '</a>');
+                $this->assertNoRaw('<a href="' . Html::escape($url3) . '">' . Html::escape($url3) . '</a>');
                 $this->assertEscaped($url1);
                 $this->assertEscaped($url2);
+                $this->assertEscaped($url3);
               }
             }
             break;
@@ -484,7 +499,7 @@ function testLinkSeparateFormatter() {
       'field_name' => $field_name,
       'entity_type' => 'entity_test',
       'type' => 'link',
-      'cardinality' => 2,
+      'cardinality' => 3,
     ));
     $this->fieldStorage->save();
     FieldConfig::create([
@@ -508,20 +523,25 @@ function testLinkSeparateFormatter() {
       ->setComponent($field_name, $display_options)
       ->save();
 
-    // Create an entity with two link field values:
+    // Create an entity with three link field values:
     // - The first field item uses a URL only.
     // - The second field item uses a URL and link text.
+    // - The third field item uses a fragment-only URL with text.
     // For consistency in assertion code below, the URL is assigned to the title
     // variable for the first field.
     $this->drupalGet('entity_test/add');
     $url1 = 'http://www.example.com/content/articles/archive?author=John&year=2012#com';
     $url2 = 'http://www.example.org/content/articles/archive?author=John&year=2012#org';
+    $url3 = '#net';
     // Intentionally contains an ampersand that needs sanitization on output.
     $title2 = 'A very long & strange example title that could break the nice layout of the site';
+    $title3 = 'Fragment only';
     $edit = array(
       "{$field_name}[0][uri]" => $url1,
       "{$field_name}[1][uri]" => $url2,
       "{$field_name}[1][title]" => $title2,
+      "{$field_name}[2][uri]" => $url3,
+      "{$field_name}[2][title]" => $title3,
     );
     $this->drupalPostForm(NULL, $edit, t('Save'));
     preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
@@ -560,18 +580,29 @@ function testLinkSeparateFormatter() {
             $expected .= '<div class="link-url"><a href="' . Html::escape($url) . '">' . Html::escape($url_title) . '</a></div>';
             $expected .= '</div>';
             $this->assertRaw($expected);
+
+            $url = $url3;
+            $url_title = isset($new_value) ? Unicode::truncate($url, $new_value, FALSE, TRUE) : $url;
+            $title = isset($new_value) ? Unicode::truncate($title3, $new_value, FALSE, TRUE) : $title3;
+            $expected = '<div class="link-item">';
+            $expected .= '<div class="link-title">' . Html::escape($title) . '</div>';
+            $expected .= '<div class="link-url"><a href="' . Html::escape($url) . '">' . Html::escape($url_title) . '</a></div>';
+            $expected .= '</div>';
+            $this->assertRaw($expected);
             break;
 
           case 'rel':
             $rel = isset($new_value) ? ' rel="' . $new_value . '"' : '';
             $this->assertRaw('<div class="link-url"><a href="' . Html::escape($url1) . '"' . $rel . '>' . Html::escape($url1) . '</a></div>');
             $this->assertRaw('<div class="link-url"><a href="' . Html::escape($url2) . '"' . $rel . '>' . Html::escape($url2) . '</a></div>');
+            $this->assertRaw('<div class="link-url"><a href="' . Html::escape($url3) . '"' . $rel . '>' . Html::escape($url3) . '</a></div>');
             break;
 
           case 'target':
             $target = isset($new_value) ? ' target="' . $new_value . '"' : '';
             $this->assertRaw('<div class="link-url"><a href="' . Html::escape($url1) . '"' . $target . '>' . Html::escape($url1) . '</a></div>');
             $this->assertRaw('<div class="link-url"><a href="' . Html::escape($url2) . '"' . $target . '>' . Html::escape($url2) . '</a></div>');
+            $this->assertRaw('<div class="link-url"><a href="' . Html::escape($url3) . '"' . $target . '>' . Html::escape($url3) . '</a></div>');
             break;
         }
       }