Problem/Motivation

Drupal.extlink.attach() in js/extlink.js adds nofollow, noopener, and noreferrer to external links in three separate blocks. Two of them check the existing rel value before appending:

- noopener block: checks val.indexOf('noopener') first.
- nofollow block: checks val.indexOf(target) === -1 first.

The noreferrer block does not perform this check in either 2.x or 3.x — it unconditionally runs:

externalLinks[i].setAttribute('rel', `${val} noreferrer`);

Normally this goes unnoticed because extlink marks processed links with a data-extlink attribute and only scans a:not([data-extlink]) on each attach. But Drupal.extlink.applyClassAndSpan(), which sets that marker, explicitly excludes any link containing an <img> or <svg> when extlink_img_class is disabled (the default):

linksToProcess = links.filter((link) => link.querySelector('img, svg') === null);

So a link wrapping an inline SVG/IMG icon (e.g. a social-share icon) never gets data-extlink, and is reprocessed on every subsequent Drupal.attachBehaviors() call over that DOM region — e.g. during Big Pipe placeholder replacement (which only occurs for authenticated/uncacheable sessions), AJAX commands, etc. On each pass, nofollow/noopener correctly no-op, but noreferrer piles on again, producing e.g.:

rel="noopener nofollow noreferrer noreferrer"

Steps to reproduce

  1. Create an external link that wraps an inline <svg> (or <img>) icon, e.g. a social-share link with an inlined icon.
  2. Leave Extlink's "Apply changes to the link classes on img/svg links" (extlink_img_class) disabled (default), with nofollow/noopener/noreferrer all enabled.
  3. Load the page as an authenticated user with Big Pipe active (or otherwise trigger a second Drupal.attachBehaviors() pass over that region — e.g. an AJAX response).
  4. Inspect the link's rendered rel — noreferrer appears more than once; nofollow/noopener appear only once.

Proposed resolution

Give the noreferrer block the same existing-value guard already used by nofollow:

  if (drupalSettings.data.extlink.extNoreferrer) {                                                                                                                                                                                                                                                                                                                                                                                                                                             
    externalLinks.forEach((link, i) => {                                                                                                                                                                                                                                                                                                                                                                                                                                                       
      if (drupalSettings.data.extlink.extExcludeNoreferrer && extExcludeNoreferrer.test(link.getAttribute('href'))) {                                                                                                                                                                                                                                                                                                                                                                          
        return;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
      }                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               
      const val = link.getAttribute('rel');                                                                                                                                                                                                                                                                                                                                                                                                                                                    
      if (val === null || typeof val === 'undefined') {                                                                                                                                                                                                                                                                                                                                                                                                                                        
        externalLinks[i].setAttribute('rel', 'noreferrer');                                                                                                                                                                                                                                                                                                                                                                                                                                    
        return;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
      }                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        
      if (val.indexOf('noreferrer') === -1) {                                                                                                                                                                                                                                                                                                                                                                                                                                                  
        externalLinks[i].setAttribute('rel', `${val} noreferrer`);                                                                                                                                                                                                                                                                                                                                                                                                                             
      }                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        
    });                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        
  }  

Additional Notes

The deeper issue is that img/svg-wrapped links are always treated as "unprocessed" across repeated attach cycles, since they're excluded from the data-extlink marker. The guard above fixes the visible symptom; longer-term it may be worth switching from the custom data-extlink marker to Drupal core's once() utility so every behavior here is naturally idempotent.

I worked with @chazchumley to identify this issue in one of our codebases. The additional noreferrer was identified during testing of theme changes. We utilized Claude Sonnet 5 to pinpoint the issue. And confirmed it is present in both the 2x and 3x branches. Claude was also used for the writeup. Chaz called out that this most likely goes unnoticed because he only came across it when he clicked cancel on the notice and was presented with a second notice. We opted not to create a patch as the user impact is significantly low but felt it was noteworthy enough to provide a bug-report for you all to do with as you please. Also happy to create and validate a MR on on our codebase if this is worthy enough to pickup.

Issue fork extlink-3614677

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

pbabin created an issue. See original summary.

sriram_s made their first commit to this issue’s fork.

sriram_s’s picture

Status: Active » Needs review

Ran into this on 11.4.4, and you don't actually need Big Pipe to see it. Both settings involved are shipped defaults, so an icon link leaves first paint already at rel="noreferrer noreferrer noreferrer" and gains one more per attachBehaviors pass. A text link beside it stays clean, which confirms your read: no data-extlink, so it keeps coming back through a:not([data-extlink]).

I've put your guard up as an MR against 2.0.x, with a regression test added to ExtlinkNoReferrerTest covering an image link, an inline svg link and a text control.

smustgrave’s picture

Status: Needs review » Needs work

Left comments but read like ai to me, which is fine to use but needs ti be disclosed per https://www.drupal.org/docs/develop/issues/issue-procedures-and-etiquett...

sriram_s’s picture

Status: Needs work » Needs review

Reworked per your review. The test now extends testExtlinkNoreferrerExclusion instead of sitting in its own method, and the sprintf and cast are gone in favour of the findLink and assertTrue style used just above it. The image link hangs off google.com, since example.com is excluded from noreferrer in that test and would have proved nothing.

Ran it locally against 2.0.x on Drupal 11.4.4: fails without the guard, passes with it, and the full suite comes out at 31 tests and 476 assertions with no failures. phpcs and phpstan clean. Moving this back to needs review.

AI-Generated: Yes. I used Claude to help trace the cause and to draft the test and my comments here. The reproduction and every test run are my own, done locally. Should have said so upfront.

smustgrave’s picture

Just reiterating 0 issue using claude (I do too) just have to mention :)

  • smustgrave committed 9142992f on 2.0.x authored by sriram_s
    Issue #3614677 by pbabin, sriram_s: Do not repeat noreferrer in rel on...
smustgrave’s picture

Status: Needs review » Fixed

Thanks!

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.