Creating an SEO Friendly WordPress Theme – pt.1

WordPress is already partially SEO friendly “out of the box”, and with numerous plugins a theme can be made to be a search engine’s best friend. But we are making our theme from scratch, so instead of relying on a plugin, why don’t we create our new WordPress theme to be SEO friendly from the outset?

In part 1 of this series we will be looking at creating a simple theme (without style) and examining the head section of the standard markup created by WordPress to see where we can improve it’s relationship with search engines.

Before I get started I should point out that I will not be doing a “Getting started” style tutorial on creating themes. There are hundreds, if not thousands, of such tutorials plastered all over the internet and I sure if you used Google you would be able to find one if you really wanted. Instead, I like to jump in and actually start making things (don’t worry, I do have a plan… somewhere). I will try to explain important things – what things are, why we need that, etc, etc – but I wont be showing you file hierarchies and stuff. I also assume that you have access to the server side files of wordpress – preferably on a test installation on you own computer. (Ask how and I will  help, but again, plenty of tutorials on Google already :) ).

Ok, on with the show:

1. Getting Started

To begin with we need to create a number of files that are essential for all themes. So, navigate (file browser, NOT web browser) to your WordPress installation and open the folder “wp-content” and then the “themes” folder.

The folders you now see each represent a theme. You will probably have folders for “twentyten” and “twentyeleven” which are the default themes provided by wordpress. If you want you can take a look at these themes code (infact WordPress recommend it) but remember to come back.

We want to create a new folder for our theme. I’m calling mine “mytheme” for simplicity. Create the folder, and open it. Now we need to create two empty files to begin with: “index.php” and “style.css”.

2. Birth of a theme

Now it’s time to make WordPress recognise our theme (yes, I know it’s a bunch of empty files at the moment).

Open the “style.css” file and enter information similar to below:

/*
Theme Name: My Theme
Theme URI: http://mikesaccount.com/
Description: This is my SEO friendly theme
Author: Michael Smith
Version: 0.1
Tags: seo

License:
License URI:
*/

Now, if you go to your admin panel in your WordPress installation (web browser this time) and select “appearance->themes” from the menu your theme should be listed as available.

That’s all we need to do in “style.css” for now. As I mentioned in the second paragraph this tutorial is without style :P .

3. It’s all loopy to me

The crux of what happens within a WordPress theme occurs in what is known as “The Loop”. This is, as the name suggests, a loop, in which your blog posts are queried and displayed. On a page displaying a single post, or a “page”, the loop only queries that single post, on a page displaying a list of posts (i.e. home, archives, categories, etc) the loop queries all the posts for that page.

Open “index.php” and enter the following code (I will explain it afterwards):

<?php get_header(); ?>

<?php if ( have_posts() ): ?>
	<?php while ( have_posts() ): ?>
		<?php the_post(); ?>

<h2><a href="<?php the_permalink() ?>" rel="bookmark" title="Read <php the_title_attribute(); ?>"><?php the_title(); ?></a></h2>
<?php the_content( 'Continue reading '.get_the_title() ); ?>

	<?php endwhile; ?>
<?php else: ?>
	<p>No posts to display</p>
<?php endif; ?>

<?php get_sidebar(); ?>
<?php get_footer(); ?>

Ok, a quick explanation:

Line 1: “get_header()” is a WordPress function that loads the “header.php” file for each theme. We haven’t created one yet so the default header file is used instead. (codex)

Lines 2&3: “have_posts();” checks that there are more posts to display, so those two lines are saying: “if there are posts to display then while there are posts to display, loop.” (codex)

Line 7: Here we show the title of the current post. The title is used as a link to to the posts “permalink”. (codex: the_permalink, the_title_attribute, the_title)

Line 8: “the_content()” displays the content of the post. The “Continue reading . the_title()” is a string we are using to display when the “<!–more–>” tag is used in a post. (codex)

Line 15: “get_sidebar()” loads the “sidebar.php” file for each theme. Again, we haven’t created this yet so the WordPress default sidebar is used. (codex)

Line 16: “get_footer()”, as you can probably guess, loads the “footer.php” file for each theme, and, no, we don’t have one of them yet either. (codex)

You can view your site if you like. There’s quite a but there considering we’ve only done 16 lines so far. We have also achieved our first step towards making the theme SEO friendly. When we linked to our post using the post title we included a “title” attribute which contains the post’s title (as well as being between the “<a></a>” tags. We have also done the same with with the “continue reading” link by appending “get_the_title()” to it.

4. Adding some content Before continuing I think we should add some content to our blog, just so it actually has some content to work with. Don’t worry if you can’t think of anything to write (I never can), instead I have found a very useful site which will generate some latin dummy text for us. Head over to lipsum.com to get the text.

I am creating three dummy posts on my blog. You can do however many you like, or you can even skip this step if you really want to. Oh, I’m also deleting the “Hello World!” post provided by WordPress. One more thing, when adding (or editing) your posts, if you do not want you whole post to display on the homepage you can add a “<!–more–>” tag into the html editor where you want to break the text. So, when I add a new post, I click the “HTML” tab on the post editor and insert a “<!–more–>” tag after the first paragraph or two.

5. Jumping in head first

“So much for looking at the head section…”. Hmm… okay. I may have gone on a bit about not creating a “getting started” tutorial and almost creating one. But we did need something to work with (that’s my excuse, and I’m sticking to it!) We now need to create two more files in our theme’s directory: “header.php” and “footer.php”. These will be used to override the default WordPress header and footer. Create them, then open “header.php”. Insert the following code into it:

<?xml version "1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>

<?php wp_head(); ?>

</head>
<body>

	<div id="header">
		<h1><?php bloginfo('name'); ?></h1>
		<p><small><?php bloginfo('description'); ?></small></p>;
	</div>

Quick explanation:

Line 7. “wp_head()” automatically renders head meta information for us (although not everything! More in a minute) (codex)

Lines 13&14. “bloginfo()” displays information about our blog. In our case we asking for our blog’s name and it’s description.

We now need to add the following to our “footer.php” file:

</body>
</html>

I’m hoping I don’t need to explain what I did there. :)

Ok. Open your WordPress homepage and mavel over your blog – not for too long though, we have work to do. View the page source. For this tutorial we are only interested in the head section. It should look something like:

<head>

<meta name='robots' content='noindex,nofollow' />
<link rel='stylesheet' id='admin-bar-css'  href='http://localhost/wordpress/wp-includes/css/admin-bar.css?ver=20110622' type='text/css' media='all' />
<script type='text/javascript' src='http://localhost/wordpress/wp-includes/js/l10n.js?ver=20101110'></script>
<link rel="EditURI" type="application/rsd+xml" title="RSD" href="http://localhost/wordpress/xmlrpc.php?rsd" />
<link rel="wlwmanifest" type="application/wlwmanifest+xml" href="http://localhost/wordpress/wp-includes/wlwmanifest.xml" />
<link rel='index' title='MikesAccount.com' href='http://localhost/wordpress' />
<meta name="generator" content="WordPress 3.2.1" />
<style type="text/css" media="print">#wpadminbar { display:none; }</style.
<style type="text/css" media="screen">
	html { margin-top: 28px !important; }
	* html body { margin-top: 28px !important; }
</style>

</head>

Wow. What a mess! Not to worry though, a lot of those lines are related to the admin bar at the top when signed in. Therefore, we can ignore lines 8,9 and 14-18. These won’t be visible to search engines as they will never (hopefully) be signed into our blog.

Lines 10 and 11 are used for external editors. I don’t use them so I will be removing these shortly.

Line 7 is only there because I specified that the site should not be accessible to search engines during set-up. On a live site, this will not be there. We will be creating our own “robots” rules soon.

On our home page, click on a post title to load the post, and again view the source. Again WordPress has added some meta tags, including “alternate”, “index”, “start”, “prev”, “next”, “canonical” all of which are okay – we will be keeping them – but search engines don’t read them.

There are a few things missing though. Where is the title and description? These are essential for SEO. Well, WordPress isn’t psychic and doesn’t know what you want to put in these fields. The standard way of filling these is populating them with the blog name and the blog description, which isn’t really SEO friendly. Google insists that every page should have a unique description associated with that page but WordPress does not offer this “out of the box”.

The usual way of adding a title and description to the page head is to add it ourselves in the head section of our page. We’re not going to do that as we’re going to be adding dynamic headings a titles. To do this, we need to create a file named functions.php. Go ahead and create it.

Let’s begin by removing some “junk” from the default head. Add the following to “functions.php”:

<?php

remove_action( 'wp_head', 'wlwmanifest_link' );
remove_action( 'wp_head', 'wp_generator' );
remove_action( 'wp_head', 'rsd_link' );

If you want to use external editors for your blog then do not include lines 3 and 4. I have also removed the generator meta tag – mainly because I don’t want people seeing which version of WordPress I’m using (just in case a security issue comes up with that certain version, etc). (codex)

Next we need to add title and description to the “head” action. We do this by creating functions and “hooking” them to the “head” action. Add the following code to “functions.php”:

remove_filter( 'term_description', 'wpautop' );

function make_safe( $text ){
	$text = str_replace( array( "\r\n","\n\r","\n","\r","\t" ), " ", $text );
	$text = str_replace( array( "\"", "'" ), "", $text );
	return $text;
}

function wp_meta_title(){
	echo "".wp_title( "@", 0, "right" ).get_bloginfo( 'name' )."\n";
}

function wp_meta_description(){
	$description = "";
	if ( !is_singular() ){
		if ( is_category() &amp;&amp; category_description()){
			$description = make_safe( category_description() );
		} else {
			$description = make_safe( get_bloginfo('description') );
		}
	} else {
		global $post;
		$description = make_safe( $post-&gt;post_content );
		$description = substr( $description, 0, 155 );
	}
	echo "<meta name=\"description\" content=\"$description\" />\n";
}

add_action( 'wp_head', 'wp_meta_title' );
add_action( 'wp_head', 'wp_meta_description' );

Some explanation is required here I think:

Line 7. “remove_filter()” removes a filter from an element. In this case, because we are using “category_description()” as part of a possible description, we are removing the “<p></p>” tags from the returned string. We must remember to add them ourselves if ever using the “category_description()” function on our pages. (codex)

Lines 9-13. We create a function “make_safe” which simply filters out and new lines, tabs, and quotations from a string. Useful for text going into our description meta tag – and maybe elewhere.

Lines 15-17. This function is used to create a title for our pages. WordPress already provides a useful function “wp_title()” that dynamically creates a title (codex), we are appending the sites name to the end of this.

Lines 19-33. This function creates the description for our pages:

Line 21. “is_singular()” checks to see if the page being shown is a “post”, “page” or an attachment. (codex)

Lines 22-26. If the page isn’t singular we check to see if it is a category page, and if so does the category have a description, if so we use that description for the page description, otherwise we use the blog’s description. (codex: is_category(), category_description() )

Lines 27-31. If the page is “singular” we take the first 155 characters of the post/page content and use that as our description – this could be changed to use an excerpt though. It is worth noting that we have accessed a WordPress internal class here ($post)  to access the post_content. Otherwise this information would only have been available during the “loop”.

Line 32. We output the meta tag.

Lines 35&36. He we create hooks so that our functions are called when the wp_head() function is called, causing our title and description meta tag to be added to our head section.

And now, for the keywords meta tag… it will be non-existent. That’s right, we are not going to add one. We could have taken the post’s tags, or blog categories and used them, but because Google ignores the keywords meta tag it’s not really worth it.

Load your blog and check the html source. You should now find a title and description inserted automatically for you.

Well, that’s enough about the head section for now. Next time we will be adding more to the loop. Until then, please ask questions and suggest improvements etc. I am still learning too :)

About mike

Michael Smith is a mature student currently studying towards a BSc in Computer Systems Engineering. He has a keen interest in all things technical and always strives to discover new technologies. As a hobby, Mike likes to do a bit of web design and games programming.
This entry was posted in Tutorials, Web Stuff and tagged , . Bookmark the permalink.