2

I want to add nofollow attribute to only external links in the content of the post. Internal links should stay follow.

So, can I use wp_rel_nofollow() to make only external links nofollow? Or do I need to use another method?

2

wp_rel_nofollow() add nofollow attribute to all links so we can not use it or may be I am not sure how.

You can use this function to add rel="nofollow" to all external links. This function will check all links in content against your blog/website URL (as internal domain) and add nofollow attribute if both does not match.

function add_nofollow_external_links( $content ) {
    return preg_replace_callback( '/<a>]+/', 'auto_nofollow_callback', $content );
}
function auto_nofollow_callback( $matches ) {
    $link = $matches[0];
    $site_link = get_bloginfo('url');
    if (strpos($link, 'rel') === false) {
        $link = preg_replace("%(href=S(?!$site_link))%i", 'rel="nofollow" $1', $link);
    } elseif (preg_match("%href=S(?!$site_link)%i", $link)) {
        $link = preg_replace('/rel=S(?!nofollow)S*/i', 'rel="nofollow"', $link);
    }
    return $link;
}
add_filter( 'the_content', 'add_nofollow_external_links' );

not tested.

  • Does it cause any negative impact on performace compared to wp_rel_nofollow()? – Gixty Nov 20 '14 at 17:15
  • I don't know why it should. It's mostly the same as using wp_rel_nofollow() except it has a additional check for external domain. That's it but if you add tons of domains to check and exclude then it probably will. – Robert hue Nov 20 '14 at 17:26
  • Great! I came up with a similar solution and just posted it. I did however accepted your answer because of your help and pointing me to the right direction. – Gixty Nov 20 '14 at 18:00
1

Based on @Robert hue's answer and the wordpress functions wp_rel_nofollow() and wp_rel_nofollow_callback I came up with this very similar solution that works for me because for some reason Robert's wasnt adding the nofollow attribute to the link.

function add_rel_nofollow( $text ) {
    // This is a pre save filter, so text is already escaped.
    $text = stripslashes($text);
    $text = preg_replace_callback('|<a (.+?)>|i', 'add_rel_nofollow_callback', $text);
    //$text = wp_slash($text); //I had to remove this because it was adding undesired backslashes to the output
    return $text;
}

function add_rel_nofollow_callback( $matches ) {
    $text = $matches[1];
    $site_link = get_bloginfo('url');

    if (strpos($text, 'rel') === false) {
        $text = preg_replace("%(href=S(?!$site_link))%i", 'rel="nofollow" $1', $text);
    } elseif (preg_match("%href=S(?!$site_link)%i", $link)) {
        $text = str_replace(array(' rel="nofollow"', " rel='nofollow'"), '', $text);
    }       

    return "<a $text rel=\"nofollow\">";
}
add_filter( 'the_content', 'add_rel_nofollow' );

This adds a rel="nofollow" attribute to all previously published and future posts.

Regarding performance, I asked @Roberthue the same question and this is what he said:

I don't know why it should. It's mostly the same as using wp_rel_nofollow() except it has a additional check for external domain. That's it but if you add tons of domains to check and exclude then it probably will. – Robert hue

  • Do you mind adding more info on exactly how this works? Does it add the tags only on new posts or does it work on previously published posts? How about performance? – Christine Cooper Nov 20 '14 at 18:01
  • Please see the answer I just posted... – Christine Cooper Nov 20 '14 at 18:07
  • @ChristineCooper I just edited my answer. I hope that helps. It does add a rel="nofollow" attribute to all previously published and future posts. And it should not impact performace unless you check for many domains as Robert Hue said. – Gixty Nov 20 '14 at 18:07
0

I use the following function to add nofollow tags to external links:

add_filter( 'the_content', 'nofollow_enternal_links');

function nofollow_enternal_links( $content ) {

    $regexp = "<a\s[^>]*href=(\"??)([^\" >]*?)\\1[^>]*>";
    if(preg_match_all("/$regexp/siU", $content, $matches, PREG_SET_ORDER)) {
        if( !empty($matches) ) {

            $srcUrl = get_option('home');
            for ($i=0; $i < count($matches); $i++)
            {

                $tag = $matches[$i][0];
                $tag2 = $matches[$i][0];
                $url = $matches[$i][0];

                $noFollow = '';

                $pattern = '/rel\s*=\s*"\s*[n|d]ofollow\s*"/';
                preg_match($pattern, $tag2, $match, PREG_OFFSET_CAPTURE);
                if( count($match) < 1 )
                    $noFollow .= ' rel="nofollow" ';

                $pos = strpos($url,$srcUrl);
                if ($pos === false) {
                    $tag = rtrim ($tag,'>');
                    $tag .= $noFollow.'>';
                    $content = str_replace($tag2,$tag,$content);
                }
            }
        }
    }

    $content = str_replace(']]>', ']]&gt;', $content);
    return $content;

}

This works site wide and targets all posts, even the published ones.

This approach is different to the other working one posted.

I'm posting this to check if anyone can confirm whether if this or the other approach is better for performance.

  • 1
    I certainly dont know if yours performs better; however, I would stick with wordpress default functions. I would like to know what others have to say. – Gixty Nov 20 '14 at 18:13
  • @Christine I think the code is from plugin Nofollow for external link. You can use this too... It does the same but there are no considerable difference in both methods in terms of performance. – Robert hue Nov 20 '14 at 19:28
0

Neither Gixty's nor Robert's code worked for me. Robert's one didn't even match correctly links, and even with the correct pattern didn't check whether the link was internal or external, and added the nofollow to all links, whereas Gixty's one correctly added the nofollow, but again to all links, not just internal ones.

This is my code, which works fine for me and only adds the rel nofollow to internal links. Also, notice that it doesn't touch links which do already have the rel attribute, whatever value it may have.

function add_nofollow_external_links( $content ) {
    return preg_replace_callback( '|<a (.+?)>|i', 'add_nofollow_callback', $content );
}

function add_nofollow_callback( $matches ) {
    $text = $matches[1];
    $site_link = get_bloginfo( 'url' );

    //If this is an internal link, don't touch it
    if( strpos( $text, $site_link ) ) {
        return "<a $text>";
    }     

    //If this doesn't have the rel attribute, append the nofollow
    if( strpos( $text, 'rel' ) === false ) {
        $text = preg_replace( "%(href=S(?!$site_link))%i", 'rel="nofollow" $1', $text );
    } 

    return "<a $text rel=\"nofollow\">";
}
0

I am using following code to make all external links no follow and this code works.

add_filter('the_content', 'my_nofollow');
add_filter('the_excerpt', 'my_nofollow');

function my_nofollow($content) {
return preg_replace_callback('/<a[^>]+/', 'my_nofollow_callback', $content);
}
function my_nofollow_callback($matches) {
$link = $matches[0];
$site_link = get_bloginfo('url');

if (strpos($link, 'rel') === false) {
    $link = preg_replace("%(href=\S(?!$site_link))%i", 'rel="nofollow" $1', $link);
} elseif (preg_match("%href=\S(?!$site_link)%i", $link)) {
    $link = preg_replace('/rel=\S(?!nofollow)\S*/i', 'rel="nofollow"', $link);
}
return $link;
}
0

The other answers don't work in all cases and/or change links when they shouldn't and/or don't reliably check for the rel attribute if it exists.

This solution is a bit more comprehensive and allows for more flexibility.

// Takes an string of html attr and adds an attribute value; if attr is present and $replace=false, space and the new value is added to end of the attribute; otherwise attr is added to end.
function inject_html_attr($attr_str, $new_attr_name, $attr_value, $replace=false) {
    return preg_replace_callback(
      '/(((?:^|\s)'.$new_attr_name.'=[\'"])(.*?))([\'"])|$/i', 
      function($m)use($new_attr_name,$attr_value, $replace){
        if( $m[0] ){
            $m[1] = $replace ? $m[2] : $m[1].($m[3]?' ':'');
        }else{
            $m[1] = ' '.$new_attr_name.'="'; 
            $m[4] = '"';
        }
        return $m[1].$attr_value.$m[4];
      }, 
    $attr_str, 1 );
}

add_filter('the_content', 'apply_external_link_markup');
function apply_external_link_markup($content) {
    // Assumes attributes are quoted (single or double)
    return preg_replace_callback(
      '/<a ([^>]*)(href=(["\'])(?:https?:)?\\/\\/([^\\/\'">]+)(.*?)\3)([^>]*)>/i', 
      function($m) {
        if( $m[4] === $_SERVER['HTTP_HOST'] )
            return $m[0];
        $attr = ' '.trim(trim($m[1]).' '.trim($m[6]));
        //$attr = inject_html_attr( $attr, 'class', 'external-link' );
        //$attr = inject_html_attr( $attr, 'target', '_blank', true );
        $attr = inject_html_attr( $attr, 'rel', 'nofollow', true );
        return '<a '.$m[2].$attr.'>';
      }, 
    $content );
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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