Jekyll ExtLinks plugin

This Jekyll plugin adds custom attributes to external links. For example, you can add rel="nofollow"to all external links by default (with exceptions if you need them), or something like class="external". You can also use it to add target="_blank" to links, but generally it is not recommended as it leads to bad user experience. Multiple attributes are allowed.

The Nokogiri gem is required. If you experience any problems installing Nokogiri («ERROR: Failed to build gem native extension», etc.), run gem update --system and try again.


Update: Jekyll ExtLinks plugin has been released to RubyGems: rubygems.org/gems/jekyll-extlinks. Use gem install jekyll-extlinks to install it.

It's also available on GitHub: github.com/d-ogarkov/jekyll-extlinks.


Download: extlinks.rb

Source code:

# Jekyll ExtLinks plugin
# Adds custom attributes like rel="nofollow" to all external links.
#
# 1. Install: put it in your _plugins folder inside your Jekyll project
# source root. Install nokogiri (gem install nokogiri).
#
# 2. Configure plugin in _config.yml. Notice the indentation matters. Example:
#
# extlinks:
#   attributes: {rel: nofollow, target: _blank}
#   rel_exclude: ['host1.com', 'host2.net']
#
# (attributes are required - at least one of them, rel_exclude is optional)
# Relative links will not be processed.
# Links to hosts listed in rel_exclude will not have the 'rel' attribute set.
# Links which have the 'rel' attribute already will keep it unchanged, like
# this one in Markdown:
# [Link text](http://someurl.com){:rel="dofollow"}
#
# 3. Use in layouts: {{ content | extlinks }}
#
# Developed by Dmitry Ogarkov - http://ogarkov.com/jekyll/plugins/extlinks/
# Based on http://dev.mensfeld.pl/2014/12/rackrails-middleware-that-will-ensure-relnofollow-for-all-your-links/

require 'nokogiri'

module Jekyll
  module ExtLinks
    # Access plugin config in _config.yml
    def config
      @context.registers[:site].config['extlinks']
    end

    # Checks if str contains any fragment of the fragments array
    def contains_any(str, fragments)
      return false unless Regexp.union(fragments) =~ str
      true
    end

    def extlinks(content)
      # Process configured link attributes and whitelisted hosts
      if config
        if config['attributes']
          attributes = Array(config['attributes'])
        end
        if config['rel_exclude']
          rel_exclude = Array(config['rel_exclude'])
        end
      end
      # Stop if no attributes were specified
      return content unless attributes

      doc = Nokogiri::HTML.parse(content)
      # Stop if we could't parse with HTML
      return content unless doc

      doc.css('a').each do |a|
        # If this is a local link don't change it
        next unless a.get_attribute('href') =~ /\Ahttp/i

        attributes.each do |attr, value|
          if attr.downcase == 'rel'
            # If there's a rel already don't change it
            next unless !a.get_attribute('rel') || a.get_attribute('rel').empty?
            # Skip whitelisted hosts for the 'rel' attribute
            next if rel_exclude && contains_any(a.get_attribute('href'), rel_exclude)
          end
          a.set_attribute(attr, value)
        end
      end

      doc.to_s
    end

  end
end
Liquid::Template.register_filter(Jekyll::ExtLinks)
  • http://honza.poboril.cz Honza Pobořil

    How about to publish it to RubyGems, so we can use it as a gem?

  • http://ogarkov.com/ Дмитрий Огарков

    Deal. I hope to update it and publish as you suggest.

  • http://honza.poboril.cz Honza Pobořil

    Nice :-)

    I already found some points how to improve it. Maybe on Github we can colaborate on it.

  • http://honza.poboril.cz Honza Pobořil

    How is it going?

    I really use it now, because I have Jekyll theme using it and to include it in theme I need to have also plugins as a gem.

  • http://ogarkov.com/ Дмитрий Огарков

    This weekend will be my deadline, ok? :)

  • http://honza.poboril.cz Honza Pobořil

    OK :-)

    Feel free to write me if you want to help with something.