I can haz some Google love? A simple Drupal internal linking strategy.
I spent a little time today exploring some options for improving the internal linking on my blog. I'm not particularly worried about getting my site high up in the Google, but because I regularly add content it's a good sandbox for testing modules and techniques in achieving search engine friendliness.
A huge focus in SEO is getting higher ranking sites to link to yours, called 'incoming links', and the emphasis on this strategy leaves a lot of room for getting an edge using other techniques. In particular, 'internal links' - or links from one of your pages to another - is a technique way less exploited, but also much more under your control. Here's a couple of principles I've picked up over the years:
- Links from one of your pages to another one is just as valid as a link from an external site. What matters is the PageRank of the page. Think "pages" not "sites"
- The longevity of links comes into play, so rotating links aren't as effective in the long run as permanent ones
- You can link to several pages from one page without diluting the PageRank that page passes onto the others, possibly up to a hundred
So, what this calls for is a strategy for easily creating permanent links from one page to another. Also, by linking to new pages from several others you increase the speed of getting indexed (in theory).
Here's what I done:
1. Installed the Related Content module
The Related Content module allows a user to create one-way associations between one node and several others. These associations can then shown in a variety of formats on the node page, or you can use an API to get a list and do your own formatting. To use it, you first have to set up a view and that's used to display a list of nodes to choose from. I thought the implementation was pretty witty, and I've got some ideas for improvements I'd like to see. For example, automated relationships based on keywords, 2-way associations, hiding the current node (so you don't associate the node with itself), supplying a default view, adding a few more formats, and ajax-loaded views so that exposed filters can display. Doesn't look like the module hasn't gotten much love in the last year or so, but it's functional and think it's a great start.
The role this module fills in terms of search engine friendliness is that it allows nodes to permanently link to multiple other nodes. For real users, it also gives them a next step on your pages. For pages with a high bounce rate, it lays out something that might be a bit enticing.
2. Installed the Vocabulary Index module
You can set up Views to create a list of taxonomy terms with links to the term pages, or you can install Vocabulary Index, which does that out of the box, and also includes a count of nodes for each term. This exposes links to vocabulary terms, which then provide internal links back to articles. I see this as another factor in a good internal linking strategy.
3. Installed the Clean Pagination module
Clean Pagination is a module I put together that will change links from /view-list?page=2 to /view-list/2. Google does a good job at indexing query string-ed pages, but it's generally thought that using clean URLs even in pagination is a stronger method. A setting in the module will also change your pagination links to include keyword-rich, more descriptive URLs in pagination links, which get replaced out with plain numbers when the page is loaded. I'm not sure if this is a good idea or not, but makes sense from an accessibility and search engine friendly perspective. Anyway, the option is there.
4. Used the monthly archive view
A monthly archive is an excellent way to generate permanent internal links. By using a monthly archive view (export found here) and adding it as a block, you expose the index pages for spidering. It took me a while to dig this view up, so I've included the code below for Views 2 in Drupal 6. Here's the view:
$view = new view;
$view->name = 'blog_archive';
$view->description = 'Display a list of months that link to content for that month.';
$view->tag = 'default';
$view->view_php = '';
$view->base_table = 'node';
$view->is_cacheable = FALSE;
$view->api_version = 2;
$view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */
$handler = $view->new_display('default', 'Defaults', 'default');
$handler->override_option('sorts', array(
'created' => array(
'id' => 'created',
'table' => 'node',
'field' => 'created',
'order' => 'DESC',
'granularity' => 'second',
'relationship' => 'none',
),
));
$handler->override_option('arguments', array(
'created_year_month' => array(
'id' => 'created_year_month',
'table' => 'node',
'field' => 'created_year_month',
'default_action' => 'summary asc',
'style_plugin' => 'default_summary',
'style_options' => array(
'count' => 1,
'override' => 1,
'items_per_page' => '30',
),
'wildcard' => 'all',
'wildcard_substitution' => 'All',
'title' => '%1',
'relationship' => 'none',
'validate_type' => 'none',
'validate_fail' => 'not found',
'default_argument_type' => 'fixed',
),
));
$handler->override_option('filters', array(
'status' => array(
'id' => 'status',
'table' => 'node',
'field' => 'status',
'operator' => '=',
'value' => 1,
'group' => 0,
'exposed' => FALSE,
'expose' => array(
'operator' => FALSE,
'label' => '',
),
'relationship' => 'none',
),
'type' => array(
'operator' => 'in',
'value' => array(
'blog' => 'blog',
),
'group' => '0',
'exposed' => FALSE,
'expose' => array(
'operator' => FALSE,
'label' => '',
),
'id' => 'type',
'table' => 'node',
'field' => 'type',
'relationship' => 'none',
),
));
$handler->override_option('access', array(
'type' => 'none',
'role' => array(),
'perm' => '',
));
$handler->override_option('title', 'blog archive');
$handler->override_option('items_per_page', 30);
$handler->override_option('use_pager', '1');
$handler->override_option('row_plugin', 'node');
$handler->override_option('row_options', array(
'teaser' => TRUE,
'links' => TRUE,
));
$handler = $view->new_display('page', 'Page', 'page');
$handler->override_option('path', 'drupal-blog-archive');
$handler->override_option('menu', array(
'type' => 'none',
'title' => '',
'description' => '',
'weight' => 0,
'name' => 'navigation',
));
$handler->override_option('tab_options', array(
'type' => 'none',
'title' => '',
'description' => '',
'weight' => 0,
));
$handler = $view->new_display('block', 'Block', 'block');
$handler->override_option('arguments', array(
'created_year_month' => array(
'id' => 'created_year_month',
'table' => 'node',
'field' => 'created_year_month',
'default_action' => 'summary asc',
'style_plugin' => 'default_summary',
'style_options' => array(
'count' => 1,
'override' => 0,
'items_per_page' => '30',
),
'wildcard' => 'all',
'wildcard_substitution' => 'All',
'title' => '%1',
'relationship' => 'none',
'validate_type' => 'none',
'validate_fail' => 'not found',
'default_argument_type' => 'fixed',
),
));
$handler->override_option('block_description', 'Archive list');
$handler->override_option('block_caching', -1);Recent blog entries
Twitter feed:
- Any tips on where to get the word on on a press release like this? http://is.gd/dR9hC #fb3 hours ago
- Press release for the "Save Joseph" campaign http://is.gd/dR9hC #fb3 hours ago
- Added bounty for the person with the lead that lands Joseph a job. Only 1 week left in the Save Joseph campaign! http://is.gd/dKybt #fb3 hours ago
- @aplusk A friend suggested you might be able to help me. Any tips on getting the word out about this: http://savejoseph.org? Thanks!15 hours ago
- @rvtctweat The scrolling module was Views Infinite Scroll http://drupal.org/project/views_infinite_scroll16 hours ago
- 8 more days to help me find a job for an amazing artist before CERTAIN DEATH! Please send ideas! http://savejoseph.org #fb1 day ago
- How to use images for menu items in Drupal with a simple preprocessing function http://is.gd/dOXc71 day ago
- The campaign to find a kick-ass artist a job is on Twitter. Follow if you believe in the cause! http://is.gd/dMwHq #fb2 days ago
- @pcambra Thanks, I'm glad you're liking them! Cheers!2 days ago
- How to create an image based dynamic countdown timer with Imagecache http://is.gd/dKHnR2 days ago
Topics
- activism
- apple software
- book reviews
- design
- Drupal
- drupal administration
- drupal community
- drupal events
- drupal modules
- drupal resources
- freaky stuff
- geeky entertainment
- iphone ipod touch
- jquery
- learning
- life hacks
- looping
- maps
- minimalism
- music
- philosophology
- presentations
- productivity
- rambling
- restraint
- software
- theming
- treadmill desk
- usability
- usability testing
- video demonstrations
- web hosting
- original songs
- This American Life
- evernote
Blog archive
- March 2009 (2)
- April 2009 (15)
- May 2009 (10)
- June 2009 (3)
- July 2009 (3)
- August 2009 (6)
- September 2009 (2)
- October 2009 (4)
- November 2009 (3)
- December 2009 (1)
- January 2010 (1)
- March 2010 (1)
- June 2010 (1)
- July 2010 (43)










Comments
Thanks that was useful on a number of levels
Nice, I'm glad to help!
Thanks for the insight. I look forward to applying these techniques to my own websites.
My pleasure, good luck!
Very eager to learn more about SEO strategies as they relate to Drupal. Thanks for this post!
Great post, thank you.
That pagination module looks very useful.
Do you know if the related content module is at all similar to the 'similar module: http://drupal.org/project/similar?
Any advantages to the latter?
Looks like an interesting module, I'll have to experiment with it. It looks like Similar Entries auto-generates a list of related content, which is handy. This approach is good for low work overhead, but most likely doesn't take into account the link permanence issue. Google knows the lifespan of a link, and the longer it is the more weight it gets. Besides that, I definitely like the idea.
Post new comment