(Reading time: 12 – 19 minutes)
I was talking to Julie Roads the other day about post slugs, and it occurred to me I haven’t written on post slugs for quite a while. In fact, while it’s on my mind, go visit Julie and get yourself some refresh and reload.
Now that you’ve wiped your slate clean, let’s reload it with Stuff You Need To Know. Like post slugs.
Although I discuss post slugs in Blog Post Engineering in some detail, much of what follows is new material. Some of this may go into the next release of Blog Post Engineering as well.
It turns out there exists a spiffy little plugin called “SEO Slugs,” which will:
- Help you create shorter, keyword-rich slugs using your article title.
- Will not automatically change your title, so you have ultimate control (as it should be).
The SEO Slugs plugin is very old and hasn’t been updated in years. I usually recommend NOT using older plugins (for good reason), but as you will see, there isn’t anything out of date in SEO Slugs. (That’s either a testament to the strength of WordPress design, or an indictment to it’s lack of innovation. I could argue both sides. I’ll spare us all.)
This plugin is cool, because you don’t really have to do anything, it just works quietly in the background:
Since it’s (Technical) Tuesday, it’s a perfect opportunity to get all hard core about it too. So I’m going to break this stepwise for you. You will see how I attack plugin problems, and how this particular plugin works to your benefit.
SEO Slugs plugin
First, if code makes your eyes glaze over, just skip right over to where I break this plugin down one piece at a time.
Since SEO Slugs is really short, I’m listing out all 43 lines right here:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | <?php /* Plugin Name: SEO Slugs Plugin URI: http://www.vretoolbar.com/news/seo-slugs-wordpress-plugin Description: Removes common words like 'a', 'the', 'in' from post slugs to improve SEO. Version: 1.0 Author: Andrei Mikrukov Author URI: http://www.vretoolbar.com */ /* Copyright Andrei Mikrukov 2007 Licensed under the terms of the GPL version 2, see: http://www.gnu.org/licenses/gpl.txt Provided without warranty, inluding any implied warrant of merchantability or fitness for purpose. */ add_filter('name_save_pre', 'seo_slugs', 0); function seo_slugs($slug) { // We don't want to change an existing slug if ($slug) return $slug; global $wpdb; $seo_slug = strtolower(stripslashes($_POST['post_title'])); $seo_slug = preg_replace('/&.+?;/', '', $seo_slug); // kill HTML entities // kill anything that is not a letter, digit, space or apostrophe $seo_slug = preg_replace ("/[^a-zA-Z0-9 \']/", "", $seo_slug); // Turn it to an array and strip common words by comparing against c.w. array $seo_slug_array = array_diff (split(" ", $seo_slug), seo_slugs_stop_words()); // Turn the sanitized array into a string $seo_slug = join("-", $seo_slug_array); return $seo_slug; } function seo_slugs_stop_words () { return array ("a", "able", "about", "above", "abroad", "according", "accordingly", "across", "actually", "adj", "after", "afterwards", "again", "against", "ago", "ahead", "ain't", "all", "allow", "allows", "almost", "alone", "along", "alongside", "already", "also", "although", "always", "am", "amid", "amidst", "among", "amongst", "an", "and", "another", "any", "anybody", "anyhow", "anyone", "anything", "anyway", "anyways", "anywhere", "apart", "appear", "appreciate", "appropriate", "are", "aren't", "around", "as", "a's", "aside", "ask", "asking", "associated", "at", "available", "away", "awfully", "b", "back", "backward", "backwards", "be", "became", "because", "become", "becomes", "becoming", "been", "before", "beforehand", "begin", "behind", "being", "believe", "below", "beside", "besides", "best", "better", "between", "beyond", "both", "brief", "but", "by", "c", "came", "can", "cannot", "cant", "can't", "caption", "cause", "causes", "certain", "certainly", "changes", "clearly", "c'mon", "co", "co.", "com", "come", "comes", "concerning", "consequently", "consider", "considering", "contain", "containing", "contains", "corresponding", "could", "couldn't", "course", "c's", "currently", "d", "dare", "daren't", "definitely", "described", "despite", "did", "didn't", "different", "directly", "do", "does", "doesn't", "doing", "done", "don't", "down", "downwards", "during", "e", "each", "edu", "eg", "eight", "eighty", "either", "else", "elsewhere", "end", "ending", "enough", "entirely", "especially", "et", "etc", "even", "ever", "evermore", "every", "everybody", "everyone", "everything", "everywhere", "ex", "exactly", "example", "except", "f", "fairly", "far", "farther", "few", "fewer", "fifth", "first", "five", "followed", "following", "follows", "for", "forever", "former", "formerly", "forth", "forward", "found", "four", "from", "further", "furthermore", "g", "get", "gets", "getting", "given", "gives", "go", "goes", "going", "gone", "got", "gotten", "greetings", "h", "had", "hadn't", "half", "happens", "hardly", "has", "hasn't", "have", "haven't", "having", "he", "he'd", "he'll", "hello", "help", "hence", "her", "here", "hereafter", "hereby", "herein", "here's", "hereupon", "hers", "herself", "he's", "hi", "him", "himself", "his", "hither", "hopefully", "how", "howbeit", "however", "hundred", "i", "i'd", "ie", "if", "ignored", "i'll", "i'm", "immediate", "in", "inasmuch", "inc", "inc.", "indeed", "indicate", "indicated", "indicates", "inner", "inside", "insofar", "instead", "into", "inward", "is", "isn't", "it", "it'd", "it'll", "its", "it's", "itself", "i've", "j", "just", "k", "keep", "keeps", "kept", "know", "known", "knows", "l", "last", "lately", "later", "latter", "latterly", "least", "less", "lest", "let", "let's", "like", "liked", "likely", "likewise", "little", "look", "looking", "looks", "low", "lower", "ltd", "m", "made", "mainly", "make", "makes", "many", "may", "maybe", "mayn't", "me", "mean", "meantime", "meanwhile", "merely", "might", "mightn't", "mine", "minus", "miss", "more", "moreover", "most", "mostly", "mr", "mrs", "much", "must", "mustn't", "my", "myself", "n", "name", "namely", "nd", "near", "nearly", "necessary", "need", "needn't", "needs", "neither", "never", "neverf", "neverless", "nevertheless", "new", "next", "nine", "ninety", "no", "nobody", "non", "none", "nonetheless", "noone", "no-one", "nor", "normally", "not", "nothing", "notwithstanding", "novel", "now", "nowhere", "o", "obviously", "of", "off", "often", "oh", "ok", "okay", "old", "on", "once", "one", "ones", "one's", "only", "onto", "opposite", "or", "other", "others", "otherwise", "ought", "oughtn't", "our", "ours", "ourselves", "out", "outside", "over", "overall", "own", "p", "particular", "particularly", "past", "per", "perhaps", "placed", "please", "plus", "possible", "presumably", "probably", "provided", "provides", "q", "que", "quite", "qv", "r", "rather", "rd", "re", "really", "reasonably", "recent", "recently", "regarding", "regardless", "regards", "relatively", "respectively", "right", "round", "s", "said", "same", "saw", "say", "saying", "says", "second", "secondly", "see", "seeing", "seem", "seemed", "seeming", "seems", "seen", "self", "selves", "sensible", "sent", "serious", "seriously", "seven", "several", "shall", "shan't", "she", "she'd", "she'll", "she's", "should", "shouldn't", "since", "six", "so", "some", "somebody", "someday", "somehow", "someone", "something", "sometime", "sometimes", "somewhat", "somewhere", "soon", "sorry", "specified", "specify", "specifying", "still", "sub", "such", "sup", "sure", "t", "take", "taken", "taking", "tell", "tends", "th", "than", "thank", "thanks", "thanx", "that", "that'll", "thats", "that's", "that've", "the", "their", "theirs", "them", "themselves", "then", "thence", "there", "thereafter", "thereby", "there'd", "therefore", "therein", "there'll", "there're", "theres", "there's", "thereupon", "there've", "these", "they", "they'd", "they'll", "they're", "they've", "thing", "things", "think", "third", "thirty", "this", "thorough", "thoroughly", "those", "though", "three", "through", "throughout", "thru", "thus", "till", "to", "together", "too", "took", "toward", "towards", "tried", "tries", "truly", "try", "trying", "t's", "twice", "two", "u", "un", "under", "underneath", "undoing", "unfortunately", "unless", "unlike", "unlikely", "until", "unto", "up", "upon", "upwards", "us", "use", "used", "useful", "uses", "using", "usually", "v", "value", "various", "versus", "very", "via", "viz", "vs", "w", "want", "wants", "was", "wasn't", "way", "we", "we'd", "welcome", "well", "we'll", "went", "were", "we're", "weren't", "we've", "what", "whatever", "what'll", "what's", "what've", "when", "whence", "whenever", "where", "whereafter", "whereas", "whereby", "wherein", "where's", "whereupon", "wherever", "whether", "which", "whichever", "while", "whilst", "whither", "who", "who'd", "whoever", "whole", "who'll", "whom", "whomever", "who's", "whose", "why", "will", "willing", "wish", "with", "within", "without", "wonder", "won't", "would", "wouldn't", "x", "y", "yes", "yet", "you", "you'd", "you'll", "your", "you're", "yours", "yourself", "yourselves", "you've", "z", "zero"); } ?> |
Spiffy, right? I love little pieces of code like this. Pure poetry.
Let’s prise it apart and ponder it piecewise.
Let’s break it down
Once again, if you’re getting queasy, just skip right over this part to get to the goodies at the end.
And once again, why all this trouble? Couple of reasons: SEO Slugs is very short, so “Because I can” is really all the justification I need. But another, more important reason is that it illustrates a very important and powerful capability of WordPress, in a context that most intermediate WordPress bloggers can understand. That is, you. (And that includes you too Mr. El Dorado)
Start at the beginning…
…which is usually the best place to start.
In this case, on line 20 we’re kicking off with add_filter, one of the top five most important functions in WordPress. Dig into the WordPress code, add_filter is everywhere you look. Even with relatively benign functions such as register_activation_hook, scratch it deep enough, there’s a daggone add_filter lurking down in the basement.
20 | add_filter('name_save_pre', 'seo_slugs', 0); |
Here’s what add_filter does: it takes a function, here given by seo_slugs declared on line 22, and adds that function to the list of functions which get called when the name_save_pre template is invoked. That is, when you press “Save Draft,” the seo_slugs function processes your article title and emits a tasty new post slug full of keyword-rich scrumptiousness.
But suppose you like your post slug. It’s quite tasty enough, thank you. That’s cool, SEO Slugs will keeps it mitts to itself. Here’s proof:
24 25 | // We don't want to change an existing slug if ($slug) return $slug; |
All this means is that when you put your own slug in the little box, you get to keep it:
What’s next…
27 28 | global $wpdb; $seo_slug = strtolower(stripslashes($_POST['post_title'])); |
wpdb is your WordPress database. It’s another one of those things that’s everywhere, you need to make it’s acquaintance sooner or later.

The next function, strtolower takes your title and turns all your capital letters into lower case letters. Actually, it takes your title after it does a little magic on it with stripslashes, but let’s leave that rabbit hole for another day.
Next, if you have some of those cool HTML symbols in your title, like hearts ♥s and stuff ‽, we have to remove those for the post slug:
30 | $seo_slug = preg_replace('/&.+?;/', '', $seo_slug); // kill HTML entities |
preg_replace is easy: Perl-compatible regular expression, replace all matching instances.
You know how I always say, “Simple, but not easy”? Well, this is easy, but not simple.
Let’s get rid of more cruft people like me put in our blog post titles, like punctation marks, etc.
31 32 | // kill anything that is not a letter, digit, space or apostrophe $seo_slug = preg_replace ("/[^a-zA-Z0-9 \']/", "", $seo_slug); |
Self-explanatory, if you grok regular expressions. ^a-zA-Z0-9 means get rid of everything not a through z, A through Z or 0 through 9. Easy peasy. (But not simple.)
Here’s where we get to the good stuff. Our post slug is all prepped, now it’s time to strip out the stop words. Words like “a” and “the” and “and” which are boring and unrankable. Seriously, trying to rank for the word “the” would be pretty hard.
31 32 33 34 35 36 | // Turn it to an array and strip common words by comparing against c.w. array $seo_slug_array = array_diff (split(" ", $seo_slug), seo_slugs_stop_words()); // Turn the sanitized array into a string $seo_slug = join("-", $seo_slug_array); return $seo_slug; |
Picking up the pace, line 32 matches your title against an array of stop words, and deletes everything in your title that is also in the array. Line 34 puts it all back together, this is what you see in your permalink after you save or publish your blog post. Line 36 is the seo_slugs function giving your brand new slug back to WordPress.
And there you have it, the entire SEO Slugs plugin.
Give it try, see what you think.
UPDATE: Don’t know why I didn’t think of this before.
“Why all this toil and trouble?”
That’s a pretty good question.
In part, I’m showing you exactly what I do when I teach myself. I just go through the code one line at a time, learn what each line does, then (the hard part) figure out why that line is necessary.
Understanding source code requires an investment of time and mental energy. While I eat this stuff two or three meals per day, it’s not quite to some people’s taste, and others are downright allergic to the very notion.
But there are rewards.
One of which is overcoming all fear of WordPress coding. Well, at least most fear. There is some scary sh*t in the WordPress core, but you never need look at that if you just stick to WordPress plugins.
Knowledge builds on itself incrementally. Learning a half dozen of the most common functions you find in plugins goes a long way to stomping that learning curve flat. This is usually irrelevant, I admit, except when it’s Zero Dark Thirty and your blog is down. And you don’t know why. Then knowing a little WordPress seems pretty smart.
In any case, between this article and Blog Post Engineering, you have a grip on post slugs. Provided you have a grip on Blog Post Engineering… 
Lagniappe
July 17, 2010, and I’ve put a full week into the next release of Blog Post Engineering. Julie Angelos just pinged Top 10 Reasons WordPress is the future of the internet, with a CommentLuv link looking like this:ㄥ❀∨モ criticism ♥.
How could anyone resist that?
Now, I asserted that SEO Slugs removes fancy pants HTML entities, and it does. But you can still have such beasties in your post slug, just paste them in manually.
Here’s proof:

I have no idea what the effect on SEO is with such a spiffy little post slug. Neither do I care! Once in a while, just have some fun tweaking the rules.





