Blog
Organic Traffic Building
The site hit a milestone of sorts, traffic-wise, the Sunday before last. On the 19th, I logged over 100 unique visitors in one day! I was pretty excited about that, mostly because these were real human visitors who came to read my blog, as opposed to mindless robots who just come to look around.
On the same day, more than 280 pages were requested, which means that, on average, each visitor clicked on almost two other pages besides the one they landed on. Not too shabby, sez I ! Some folks might sneer at these numbers, and harrumph that they get as many hits every time they sneeze – well, that’s great, but how long has their site been online? How long have they been promoting themselves? My public debut – in person at a local event, was only three weeks prior to that, so no one besides my wife and a few relatives and friends knew the site existed before then. Consider also that I had purposefully not advertised the site online anywhere – that everyone who knew about did so by word of mouth, and yes, I was pleased with the traffic.
I quick query of my database also reveals that before that date, a total of about 200 unique visitors had come by, so in one day the “audience” grew by about 50%. How did I do that? By registering my blog at a number of blog aggregators and feed services. It seems that people came to check out the newbie, and judging by the almost triple hits-to-visitors ratio, apparently they liked what they saw enough to take a look around. Cool!
As of now, a grand total of 475 unique visitors have darkened my URL (see the PixelList page for current stats), and if you subtract the roughly 5-10 robots that stop by nightly (mostly the same ones), that’s maybe 450 humans on this planet who are aware of my site’s existence. Ok, not earth-shattering or cyber-rattling, but hey, wait till I actually start promoting this on a broader scale !
So, why am I waiting? Mostly because I feel very strongly that this site is for and about Austin, and I really want to capture the flavor of this unique culture. I want all Austinites who visit here to make no mistake about where they landed, and to be able to identify with this space on an intimate level. Although the Million Dollar Home Page was an inspiration for this endeavor, I don’t want my home page to end up looking like that one did. We can do better. This is Austin, y’all !
So, although my marketing and sales methods right now are not scalable at all (it’s just little ol’ me), I think it’s the right thing to do. For now. I am starting to make small forays by advertising in very Austin-specific blogs, but mostly I am doing direct, in-person appeals.
Speaking of which, quite by accident I’ve discovered a web site promotion technique that I have not seen mentioned anywhere – LOOK FOR A JOB ! Yes, since one of the secondary reasons for creating this site was to demonstrate my web development skills, and specifically to show I can pick up and run with just about any technology new to me (I had never seen PHP code or downloaded MySQL before this), I have this site listed as a “personal web project” at the very top of my resume. So what happens is, I e-mail the resume to a recruiter or hiring manager, and sure enough, the first thing they do is check out the web site. One time, it looked like an entire office had landed here – wow, 80 page hits in a couple of hours, all from the same IP address! That was a fun interview, let me tell you.
My daily visitor count did not stay at the 100-something level, it dropped off after the initial curiosity, which was to be expected. I am using this blog to drive traffic for now, with the goal of establishing a base to grow from, so site promotion is really about blog promotion. This works well for the early advertisers, since the Skyline is on every blog page. Also, there is no real rush, since the ads are not time-based. Just a one-time, up-front payment, and the ad is there for all future traffic to enjoy.
I am having fun with this “personal web project”. Surprisingly, even making cold sales calls does not suck as much as I thought it would, and seeing the traffic build is very addictive. Coming up with ideas and implementing them as I see fit is also way cool (especially when they feed my addiction – like the cell phone accessible statistics feature).
My immediate goals now are to sell a few more ads and continue to tweak the site. Then I will embark on expanding the AustinMania! section, to convert it into a community-driven site. Stay tuned!
Red-faced coder adds support for feeds and trackbacks in custom permalinks
Note: This is a continuation and correction of my previous post. Please be sure to read that to get the full context.
Yesterday I felt guilty for spending too much time writing code, today I feel embarrassed for not spending enough. After I implemented my new custom permalink mechanism, I thought I’d share that with the world, or at least the readers of the WordPress support
forums, in the hopes that it might be useful to someone else. Well, soon after posting my contribution, I realized I had neglected to take care of RSS feed requests for categories, and feed and trackback links for individual postings. Arghhhh!
And to make it worse, the hit counter on my laptop was moving, meaning people were reading the post – and it was wrong! So much for going to bed – I gotta fix this now!
At least the category feed fix was simple enough: create a new /feed/ subfolder under each category folder, each with the same index.php file, with this code:<?php
$path = dirname(dirname($_SERVER['PHP_SELF']));
$position = strrpos($path,'/') + 1;
$thisDir = substr($path,$position);
header("Location: ../../../redirect.php?cat=$thisDir&feed=rss2"); exit;
?>
The feed and trackback URLs for individual posts were more problematic, mainly because I had set up permalinks with the structure /name-of-post.php (for maximum SEO’ness). For individual post feeds and trackbacks, WordPress appends the folder name to the permalink URL, like so: /name-of-post.php/feed/. What this meant was that I would have to convert all my permalink files to permalink folders, so they could have sub folders. And since I had to do that manually, I might as well get rid of the .php in the folder name, since that would result in a weird-looking URL that might freak some people (or robots) out. This had the further unfortunate effect of rendering the permalink I posted on the forum obsolete. Let that be a lesson for you kids – don’t post in haste.
Curiously, the trackback link now works exactly as it did when I had the default permalinks, which is to say, not at all. At this point, I assume this is a completely different issue.
Ok, each permalink folder now contains two sub-folders: /feed/ and /trackback/. The index.php in /feed/ looks like this: <?php
$path = dirname(dirname($_SERVER['PHP_SELF']));
$position = strrpos($path,'/') + 1;
$thisDir = substr($path,$position);
header("Location: ../../redirect.php?p=$thisDir&feed=rss2"); exit;
?>
Note that this and the category feed index.php code converts all sub-feeds to the rss2 format, regardless of what the requestor asked for. This will probably break some feed readers, but I was already spending way more time on this than I should, and the main blog feed is unaffected. If I really wanted to fix this, I would probably replicate some of the functionality of wp-feed.php into the /feed/index.php files.
The index.php in /trackback/ contains this code: <?php
$path = dirname(dirname($_SERVER['PHP_SELF']));
$position = strrpos($path,'/') + 1;
$thisDir = substr($path,$position);
header("Location: ../../redirect.php?p=$thisDir&trackback=true"); exit;
?>
Finally, the redirect.php file had to be updated to support this new functionality. This is what it looks like now: <?php
require_once('wp-config.php');
$URLquery = "";
if (array_key_exists("trackback",$_GET)) {
$postName = $_GET['p'];
$postID = get_object_vars($wpdb->get_row("SELECT ID FROM $wpdb->posts WHERE post_name = '$postName' LIMIT 1"));
$postID = $postID['ID'];
header("Location: wp-trackback.php?p=$postID");
exit;
}
if (array_key_exists("cat",$_GET)) {
$catName = $_GET['cat'];
$catID = get_object_vars($wpdb->get_row("SELECT cat_ID FROM $wpdb->categories WHERE cat_name = '$catName' LIMIT 1"));
$catID = $catID['cat_ID'];
$URLquery = "?cat=$catID";
}
if (array_key_exists("m",$_GET)) {
$month = $_GET['m'];
$URLquery = "?m=$month";
}
if (array_key_exists("p",$_GET)) {
$postName = $_GET['p'];
$postID = get_object_vars($wpdb->get_row("SELECT ID FROM $wpdb->posts WHERE post_name = '$postName' LIMIT 1"));
$postID = $postID['ID'];
$URLquery = "?p=$postID";
}
if (array_key_exists("feed",$_GET)) {
$feed = $_GET['feed'];
$URLquery .= "&feed=$feed";
}
header("Location: index.php$URLquery");
?>
Am I happy with this solution? Not entirely - RSS compliance is not 100%, and I’ve got a mess of folders and sub folders in my blog file structure. Indeed, one response to my initial forum posting
calls my solution a kludge, and I have to agree. I am hoping someone steps up with a better solution, other than “switch hosts”. I’m all ears !
Finally got my custom WordPress permalinks working
Note: This post is continued and ammended in my next post. Please be sure to read that as well to get the full picture and correct code.
Boy do I feel guilty. I’ve reverted to my comfort zone, writing code, at the expense of marketing activities. I did make a few phone calls, mind you, but I need to put my salesman hat back on, and really tighten up the chin strap so it doesn’t fall off so easily next time. It’s a weakness of mine: give me a good, worthwhile challenge, and I can’t stop until it’s done. Well, at least I take comfort in that it seems I did solve a nagging problem that plagues some WordPress users.
WordPress allows you to define custom permalink structures for your posts, and this seems to work fine for most people. For example, instead of the URL for a post being in the form: /blog/?p=22, the URL can be /blog/name-of-the-post.php. The second URL is favored by search engines, since it supports the content of the page. However, this functionality relies on the web server using the “mod_rewrite” module to read the “.htaccess” file for instructions on how to perform the necessary redirections, and if you are not running your own web server, you are at the mercy of your web host to provide this functionality. Alas, my host does not, so it seemed I was stuck with ugly URLs.
My research into this problem only revealed there are some very frustrated users out there, and that even with a working mod-rewrite module, getting custom permalinks to work in WP was a hit-or-miss affair. Most of the suggested solutions were of the “reinstall everything” variety. I even ran across several threads where someone discovered they did not have the mod_rewrite module installed, so they switched hosts. Well, I didn’t want to do that, so I came up with my own workaround instead.
The way WP implements permalinks is pretty slick (when it works). Instead of actually creating a complex folder structure for all the categories, archives, and posts, all pages are really served by the same file from the same location, /blog directory/index.php, with a query appended to the URL so it knows what to serve up: a category, an archive, or an individual post. Then, when blog pages are generated, all the category, archive, and post links are written according to the custom permalink structure. So, even if the link to an archive is /blog/2006/03/, there really is no such directory, but the link works anyway because the server itself redirects the request back to /index.php, according to the instructions in the .htaccess file. One of the big advantages to this scheme is that the permalink structure can be changed at any time, and there is no need to go and re-name or re-shuffle a bunch of directories and files when you do so. The only things that change are the links on the pages, and the .htaccess file.
Ok, I could not use an .htaccess file, so I was pretty much forced to do the redirections myself. This meant actually creating the nested category and archive folders, and placing a single index.php file in each of them, that redirected the request back to the base folder, to a new file I created, that did the final redirect to index.php. Also, since the custom permalink structure I wanted was: /%postname%.php, I also had to create a file for each post, each one named name-of-post.php that also redirected requests to my new redirect.php file, so it could do the final redirect to index.php.
This sounds complicated, but it’s not that bad, and once it is set up, there is little maintenance involved. Obviously the biggest drawback is that I have to remember to create a new name-of-post.php file every time I make a new post. Also, I need to know the cleaned-up post name that WP will use for the permalink. This could probably be handled with a plug-in, but I’ll leave that as an exercise for the interested reader. For now, I’ll make theses file manually. All I have to do is copy and rename an existing one, so it’s not that bad. The same goes for any new categories I create in the future.
Another drawback to this implementation is that the final URL the user sees is the ugly one, without my custom permalink. I guess I’ll have to live with that, and since this was mostly done to appease the Google God, it should be ok. I did read that the preferred redirection
for search engines is a “301 – permanent move”, but according to the RFC 2616 - Hypertext Transfer Protocol — HTTP/1.1
documentation, this will cause the requester to update the URL that was used to the new, redirected one, which would negate any benefit from the custom URL. Instead, I opted to use the default “302 – found” header that PHP sends with the redirect, that signals to the requester that this is a temporary move that might change, so please continue using the original URL. I hope this works.
Do you suppose the Googleites went to the trouble of seeing how long a “temporary” redirect is in effect, and penalize those who use it too long? Should I cower in fear, afraid that gGod might see my impudence, and banish me from his holy index? Whatever. – Hey, if that happens, I’ll put out a press release and get all sorts of attention, right?
Will this be my last tech post? Hardly – there is plenty to do on the site still – at least 4 of the remaining to-do items are tech, and that’s even before I get into expanding AustinMania! But, I think I will be unhappy with myself I don’t get out there and sell more ads. Pixels, anyone?
This is the index.php code that went in every category folder - /blog/categories/austin/index.php, for example:<?php
$path = dirname($_SERVER['PHP_SELF']);
$position = strrpos($path,'/') + 1;
$thisDir = substr($path,$position);
header("Location: ../../redirect.php?cat=$thisDir"); exit;
?>
This is the index.php code that went in every archive folder - /blog/2006/03/index.php, for example:<?php
$path = dirname($_SERVER['PHP_SELF']);
$position = strrpos($path,'/') + 1;
$thisDir = substr($path,$position);
header("Location: ../../redirect.php?m=2006$thisDir"); exit;
?>
Every year, I’ll have to copy the previous year’s folder set, and edit this file to change the year on the last line.
This is the contents of every “name-of-post.php” file:<?php
$path = $_SERVER['PHP_SELF'];
$position = strrpos($path,'/') + 1;
$thisPost = substr($path,$position,-4);
header("Location: redirect.php?p=$thisPost"); exit;
?>
This is the redirect.php file code that receives all the redirects above:<?php
require_once('wp-config.php');
$URLquery = "";
if (array_key_exists("cat",$_GET)) {
$catName = $_GET['cat'];
$catID = get_object_vars($wpdb->get_row("SELECT cat_ID FROM $wpdb->categories WHERE cat_name = '$catName' LIMIT 1"));
$catID = $catID['cat_ID'];
$URLquery = "?cat=$catID";
}
if (array_key_exists("m",$_GET)) {
$month = $_GET['m'];
$URLquery = "?m=$month";
}
if (array_key_exists("p",$_GET)) {
$postName = $_GET['p'];
$postID = get_object_vars($wpdb->get_row("SELECT ID FROM $wpdb->posts WHERE post_name = '$postName' LIMIT 1"));
$postID = $postID['ID'];
$URLquery = "?p=$postID";
}
header("Location: index.php$URLquery");
?>
In Search of SEO
In my quest for traffic, I’ve been immersed in the esoterica of Search Engine Optimization. In a sense, following advice you find online on this subject is akin to listening to doctors about what to eat. You acknowledge that what you are told makes sense, and it is most likely good for you, and the sources seem knowledgeable and sincere. So you follow the advice, but there are no immediately visible effects. And if you are doing other things at the same time to achieve the same goal, it’s hard to tell what any one thing did to help. Sure, I lost weight when I cut back on the carbs, but I also started working out, so who’s to say which had the greater effect?
Oh, I know the server’s access logs allow you to see where people are coming from, so if I was so inclined, I could spend time analyzing them, and trying to correlate activity X with an increase in referrer A, but frankly, all I care about are the numbers on my little real-time stat counter on my laptop screen (and now, also on my cell phone screen – check out the PixelList page). Besides, such an analysis requires either the assumption that all activities have an immediate effect, or that the effect delay is known for each activity. Suppose you also did activity Y, and it’s effect delay was shorter than activity X, you could easily come to erroneous conclusions about causality.
If you become a slave to analysis, you will find yourself delaying activity Y until you know the effects of activity X, which is fine if your goal is to become an expert in these matters. If instead, like me, all you want is traffic, you’ll do X, Y, and Z right now, and maybe X again, and try Y.y for the heck of it, and start doing research on alpha and beta. Did Z work better than Y? Who cares? It didn’t hurt, and if traffic went up, so did my smile!
Without further ado, I got most of my SEO ideas from this
excellent post by Mark Bloomfield, and I added some tweaks of my own.
The name of the game in SEO seems to be “appeasing the Google God”. Like any good god, gGod is awesomely powerful, with unknowable motives, mysterious ways, and cult-like followers who’s job it is to tell the rest of us how to write virtuous code, so that maybe, if gGod smiles upon us, we too may achieve the highest levels of search results nirvana.
Ok, maybe cult-like is too strong a phrase, but some of the parallels between gGod and Zeus are pretty striking, don’t you think? Zeus had his thunder and lightning, and gGod has his search index, which he can use to smite those who displease him
. While Zeus feasted on sacrificial lambs, gGod gets his pleadings via e-mail
:
I-Newswire owner Eric Borgos:
“I e-mailed Google today to let them know about these changes, so hopefully they will add my site back,”
Good luck, Eric!
As befits the information age, gGod seems to be fixated on words (good thing, I’m fresh out of lambs), and the consistency of the “message” is as important as the sincerity of the sacrifices of yore. For a proper sacrifice, you know, the lamb itself was not enough. If you really wanted things to go your way, you had to make a sturdy pyre to set your offering on, start a good, roaring fire, and make an impassioned speech. Wailing like a baby probably didn’t hurt, either, especially if others joined you. Likewise, the content of a web page is not enough. For gGod to notice you, you need to set your prose at a sturdy URL (with words that support your content, while making a short directory stack), engulf the text with clean code, and expound on your content with relevant meta keywords, page title, and description. Wailing like a baby probably doesn’t hurt, either, especially if others joined to you with hyperlinks.
I was able to implement most of these concepts in this WordPress blog. If you are reading this on my web site (and not through an RSS feed), take a glance at the page title at the top of your browser. You will notice either the blog name and description, category name, or post title in the page title, depending on how you accessed this post (on the blog home page, as part of a category, or as a single post). This text is generated automatically for every page, it is not hard-coded for each post. Also, if you view the source code for the page, you will see the same title text at the beginning of the keywords meta tag, and the first few words of the post excerpt (if it exists; older posts without them will have the first words of the actual post) in the description meta tag. All of this was done to surround the post contents with a relevant “envelope” of data that is consistent with the topic of the post. Supposedly, gGod looks favorably upon this.
A related technique is to put the title of the post in the permalink URL. I have not been able to implement this yet, because my web host does not allow me access to the .htaccess file that is necessary to implement the redirects to make this work. I hope to be able to figure out a workaround to this soon, but I cannot spend too much time on it at the expense of other activities that may bear more fruit. I hope this does not displease gGod too much.
As far as surrounding the page content with clean code, the objective here is to get the words of the post nearer the top of the page HTML. So, I moved the Skyline area map code and related <Form> to the bottom of the page, by moving the PHP that generates this from the header template to the footer template. I also did the same with the call to generate the sidebar, it is now in the footer as well. The result of these efforts is that all the words gGod cares about are consistent in their meaning, which increases relevancy.
The changes to the blog page structures were accomplished by implementing these plugins:
Head META Description
Optimal Title
I also tweaked the title code like so:
<title><?php
if (!$Prod) print "Local ";
optimal_title(' @ ',TRUE);
bloginfo('name');
if (!optimal_title('', FALSE)) { print " > "; bloginfo('description'); }?></title>
The first line of this code prepends the word “Local” to the page title when it is running on my laptop. I got tired of not knowing which of my many open browser windows had my local test and development web site, and which had the public “production” web site. The next two lines just use the Optimal Title plugin and standard bloginfo function, and the last line adds the post description if the plugin is not available for some reason.
Will any of this make any difference in my traffic? Who knows? At the least, I’ve learned something new, I’ve experienced implementing it, and it can’t help but increase my blog rankings in the future. The journey continues, and these side trips make it that much more interesting. Onward ho!
What happened to my office?
I set up an office in a spare bedroom in my house when I got laid off at the end of February. It was nice and tidy, and it felt good. I even splurged on a $25 speaker phone so I could feel that much more professional while in my sweat pants. Over the next couple of weeks, things even stayed fairly organized, and now, look at this place!
In my defense, I’ve been extremely busy this week, what with South By Southwest Interactive
, job interviews, and tweaks to the web site all going on at once. So let’s see – Monday and Tuesday was wall-to-wall SXSW, between preparing to go (printing flyers, determining where I wanted to be when, etc), the actual conference, and the after-parties. For those of you who attend these kinds of shindigs, you know that the parties are a major portion of the value of the event. It is at the parties when you meet the most amazing people, and learn of the coolest stuff that people are actually doing, or about to do, or theorizing about. It’s a very energetic and encouraging environment, and what, it’s over already? When’s the next one?
Of course, it’s my own fault I missed half the festival. Last summer, I got it in my head that I really wanted to go see Spamalot
on Broadway. The trip itself would not be a problem – one of the perks of being a traveling professional services consultant is the airline miles and hotel points you rack up over time, not to mention that if you consistently use the Amex card enough, you can get Broadway tickets free as well. No, the problem was that every single performance of that show was completely sold out for the next 9 months, and yes, you guessed it, the first tickets I could get were for March 11th. It was a bit ironic, booking a spur-of-the-moment whim so far in the future, and who would have thunk it then that it would mean missing some excellent discussions on the state of the web? Oh well, life is the convergence of paths already chosen.
By the way, the show itself was fantastic. To get an idea of just one moment of silliness, picture a spoof of Lord of the Dance done with coconuts. You Monty Python fans know what I mean. Even my non-Monty wife, who formally wouldn’t have known a Black Knight from a Knight That Says Ni, thoroughly enjoyed the show.
Anyway, back in reality, on Wed I had two job interviews to go to, with all the attendant preparation involved, and there was a ton of things to follow up on from SXSW. Like, for example, adding my blog to blog promotion services I learned about, which meant seeing for the first time how my posts look when they come through an RSS feed, which then meant a mad scramble to fix all the problems I found (which I am very sure are all deadly sins in the blogosphere), such as coding relative paths to images! Ouch! Owww! Hey, that HURTS! Yes, I know I deserve to be flogged, but come on, have some pity on the first time blogger!
Thursday was spent on the unfortunate need to redo some code I thought I was done with, and I’ll post more on that later, but for now, I think I’ll take a break and try to straighten up a little. Of course, I’ve been thinking that for two days, but I mean it this time! Really! Only, where do I start? Maybe I should have lunch first….





