I'm just use the add_filter('the_content', function($content){}); for filtering anchor tags in a post.

Basically, I want filter anchor tags to appropriate format. I want to add custom attributes like rel="nofollow noopener" and target="_blank" if the anchor tag linked to external of my WordPress site.

Instead checking and replace if rel or target is already exists in the anchor tag, I just need to retrieve the value of href and the value of the anchor tag itself (<a>value</a>). So I can force create an anchor tags with my preferred custom attributes.

Here's my current working function:

add_filter('the_content', function($content) {
    $internal = get_home_url();

    return preg_replace_callback('#<a.*?href="([^"]*)".*?>([^>]*)</a>#i', function($match) use ($internal) {
        if(stripos($match[1], $internal) || strpos($match[1], "#") == 0) {
            return "<a href=\"$match[1]\">$match[2]</a>";
        } else {
            return "<a href=\"$match[1]\" rel=\"nofollow noopener\" target=\"_blank\">$match[2]</a>";
    }, $content);

I'm worried about performance because this function run each user read a post. Any suggestions are welcome.

  • \$\begingroup\$ How many posts with approx how many links are we talking? I don't think this would be a performance issue unless your are processing a significant number of links. (like 100s per second) \$\endgroup\$
    – Rager
    Nov 11, 2019 at 18:20
  • \$\begingroup\$ If you are worried about performance. I would say write a JavaScript function to handle displayed links. Takes all the work out of the server and onto clients computers. \$\endgroup\$
    – Rager
    Nov 11, 2019 at 18:22

1 Answer 1


I must recommend that you favor accuracy over performance. After all, what good is a fast loading site if it doesn't provide the correct/intended content.

When parsing valid html, please always leverage a good dom parser. Regex is "dom-unaware" and therefore is more vulnerable to breakage.

Here is a demonstration of how to use DOMDocument and XPath to articulately replace hyperlinks in your document in a stable manner:

Code: (Demo)

$html = <<<HTML
<a href="#">hello</a> <abbr href="sneaky.com">FYI</abbr> <a title="goodbye">later</a>
<a href=https://example.com>no quoted attributes</a>
<A href="https://external.com"
title="some title"
data-key="{\'key\':\'adf0a8dfq<>*1$4%\'">a link with data attribute</A>
this is <a title="hello">not a hyperlink</a> but simply an anchor tag
<a href="#jumpTo">Jumper</a>

$internal = 'https://example.com';

$dom = new DOMDocument; 
$xpath = new DOMXPath($dom);
foreach ($xpath->query("//a[@href]") as $node) {
    $replacementNode = $dom->createElement("a", $node->nodeValue);
    $href = $node->getAttribute('href');
    $replacementNode->setAttribute('href', $href); 
    if($href[0] != "#" && stripos($href, $internal) === false) {
        $replacementNode->setAttribute('rel', 'nofollow noopener');
        $replacementNode->setAttribute('target', '_blank');
    $node->parentNode->replaceChild($replacementNode, $node);
echo $dom->saveHTML();


<a href="#">hello</a> <abbr href="sneaky.com">FYI</abbr> <a title="goodbye">later</a>
<a href="https://example.com">no quoted attributes</a>
<a href="https://external.com" rel="nofollow noopener" target="_blank">a link with data attribute</a>
this is <a title="hello">not a hyperlink</a> but simply an anchor tag
<a href="#jumpTo">Jumper</a>

Will this be slow? Well, I presume it will be slower than regex but then that is the cost of implementing a superior processing tool. If you need to gain performance, investigate other avenues which will not spoil your content. This is not a task where a shortcut is a good idea.

  • 1
    \$\begingroup\$ +1 for the anchor tag example in multiple cases \$\endgroup\$ Nov 12, 2019 at 2:39
  • \$\begingroup\$ After trying this code, I think you missing some cases where if anchor tag has attributes href="/" that should detect as internal and href="https://external.com?utm_source=https://example.com" that should detect as external \$\endgroup\$ Nov 12, 2019 at 3:17
  • \$\begingroup\$ Extend as you need for your project. You didn't provide any sample input data, so I fabricated some. \$\endgroup\$ Nov 12, 2019 at 3:18
  • \$\begingroup\$ According to the constraints of CodeReview, all posted questions are expected to be correct and producing the desired output. My answer uses the same logic as was implemented in your posted snippeted. Now that I have answered, you are not permitted to change your posted code. \$\endgroup\$ Nov 12, 2019 at 4:39
  • \$\begingroup\$ Yes of course. BTW your answer is awesome and very helpful. \$\endgroup\$ Nov 12, 2019 at 6:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.