<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Gregg Hilferding &#187; PHP</title>
	<atom:link href="http://www.whoisgregg.com/blog/category/php/feed" rel="self" type="application/rss+xml" />
	<link>http://www.whoisgregg.com/blog</link>
	<description>Web Development, Apple Stuff, and More</description>
	<lastBuildDate>Thu, 29 Apr 2010 23:19:52 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.4</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Hacking Lessn to Track Statistics</title>
		<link>http://www.whoisgregg.com/blog/2009/08/hacking-lessn-to-track-statistics.html</link>
		<comments>http://www.whoisgregg.com/blog/2009/08/hacking-lessn-to-track-statistics.html#comments</comments>
		<pubDate>Mon, 31 Aug 2009 17:31:28 +0000</pubDate>
		<dc:creator>Gregg Hilferding</dc:creator>
				<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://www.whoisgregg.com/blog/?p=456</guid>
		<description><![CDATA[When Shaun Inman shared his URL shortening PHP script Lessn, I was pretty excited.

Right now I use kl.am, a free service from Raven SEO Tools, for all my URL shortening needs. It's awesome and 99.5% perfect. But I've really wanted to host my own for three reasons:


    I want bulk uploads with [...]]]></description>
			<content:encoded><![CDATA[<p>When <a href="http://shaun.in/man/">Shaun Inman</a> shared his <a href="http://www.shauninman.com/archive/2009/08/17/less_n">URL shortening PHP script Lessn</a>, I was pretty excited.</p>

<p>Right now I use <a href="http://kl.am/">kl.am</a>, a free service from <a href="http://raven-seo-tools.com/">Raven SEO Tools</a>, for all my URL shortening needs. It's awesome and 99.5% perfect. But I've really wanted to host my own for three reasons:</p>

<ol>
    <li>I want bulk uploads with custom short URLs so I can have predictable, product SKU short URLs.</li>
    <li>I want to be able to repoint a short URL if it's old URL changes or 404s.</li>
    <li>I don't want to compete with other users for custom URLs.</li>
</ol>

<p>Unfortunately, rolling my own will mean I miss out on all the amazing coolness of kl.am. Mine wouldn't be as slick or easy to use. However, Lessn is slick and easy to use. But it doesn't do custom URLs and it doesn't track how often people visit a short URL. So I added those features.</p>

<h3>Tracking clicks</h3>

<p>In the file /-/db.php, add this statement to create a second table:</p>

<blockquote><code>mysql_query('CREATE TABLE IF NOT EXISTS `'.DB_PREFIX.'stats` ( '.<br />
    '`id` int(11) unsigned NOT NULL auto_increment, '. <br />
    '`url_id` int(11) unsigned NOT NULL, '. <br />
    '`ts` datetime NOT NULL, '. <br />
    '`ua` text character set utf8 collate utf8_unicode_ci NOT NULL, '. <br />
    '`ref` text character set utf8 collate utf8_unicode_ci NOT NULL, '. <br />
    '`ip` int(4) unsigned NOT NULL, '. <br />
    'PRIMARY KEY  (`id`), '. <br />
    'KEY `url_id` (`url_id`) '. <br />
    ') ENGINE=MyISAM DEFAULT CHARSET=utf8;');</code></blockquote>

<p>Then, in the /index.php file, add this line right after the header('Location:'); line:</p>

<blockquote><code>mysql_query("INSERT INTO `".DB_PREFIX."stats` SET `url_id`='".$row['id']."', `ts`='".date("Y-m-d H:i:s")."', `ua`='".mysql_real_escape_string($_SERVER['HTTP_USER_AGENT'])."', `ref`='".mysql_real_escape_string($_SERVER['HTTP_REFERER'])."', `ip`='".sprintf("%u", ip2long($_SERVER['REMOTE_ADDR']))."' ");</code></blockquote>

<p>To test, go visit one of your short URLs, and then go check the stats table. You should see a new entry in the table.</p>

<p>Now, how you choose to display the data from this table is up to you. :)</p>
<hr/>Copyright &copy; 2010 <strong><a href="http://www.whoisgregg.com/blog">Gregg Hilferding</a></strong>. This feed is for personal non-commercial use only.]]></content:encoded>
			<wfw:commentRss>http://www.whoisgregg.com/blog/2009/08/hacking-lessn-to-track-statistics.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Introducing expandUrl, Redirect Resolution for the Rest of Us</title>
		<link>http://www.whoisgregg.com/blog/2009/04/introducing-expandurl-redirect-resolution-for-the-rest-of-us.html</link>
		<comments>http://www.whoisgregg.com/blog/2009/04/introducing-expandurl-redirect-resolution-for-the-rest-of-us.html#comments</comments>
		<pubDate>Mon, 27 Apr 2009 11:30:35 +0000</pubDate>
		<dc:creator>Gregg Hilferding</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[SEO]]></category>
		<category><![CDATA[Signal]]></category>
		<category><![CDATA[Social Media]]></category>
		<category><![CDATA[Webmaster]]></category>

		<guid isPermaLink="false">http://www.whoisgregg.com/blog/?p=211</guid>
		<description><![CDATA[To solve a problem I describe in detail below, I recently created a new site called expandUrl. The expandUrl™ service does a few things very well:


Tells you the "real" URL of any shortened URL (regardless of what service provides it).
Gives technical types the redirect codes involved in all the redirects for any particular link.
Discovers both [...]]]></description>
			<content:encoded><![CDATA[<p>To solve a problem I describe in detail below, I recently created a new site called <a href="http://expandurl.com">expandUrl</a>. The expandUrl™ service does a few things very well:</p>

<ul>
<li>Tells you the "real" URL of any shortened URL (regardless of what service provides it).</li>
<li>Gives technical types the redirect codes involved in all the redirects for any particular link.</li>
<li>Discovers both canonical and shorturl data for the final expanded URL.</li>
<li>Offers a fast API for quickly finding the expanded URL (and a less fast API for getting the same detailed data that is available from the web interface).</li>
</ul>

<p><strong>Heard enough?</strong> Go check it out:</p>

<p><a title="Use expandUrl™ to find the true URL" href="http://expandurl.com/"><img class="aligncenter size-full wp-image-220" style="max-width: 80%; height: auto;" title="expandurl" src="http://www.whoisgregg.com/blog/wp-content/uploads/2009/04/expandurl-big.png" alt="expandurl" /></a></p>

<p>The expandUrl™ site was built to fix a specific problem I recently discovered on one of my other sites, so you can be confident that expandUrl™ has been tested on a sample set of a couple thousand real-life URLs. However, the total number of hours I've spent building expandUrl™ can still be counted on one hand so you are bound to discover some edge cases which I haven't encountered.</p>

<p>I appreciate all bug reports to be posted below. Security concerns should also be posted here, but I will pre-moderate those until they are fixed. (Not to hide them, but to give me the opportunity to fix them before they are exploited.)</p>

<h3>Eating my own dog food</h3>

<p>I run a site which aggregates RSS feeds from a few dozen different sources. One of the included sites accidentally let their domain name expire and had to move to a new domain name. This particular site also hosted their RSS feed with Feedburner.</p>

<p>When the site changed domain all the URLs were broken. You may be thinking, "Well, duh. There's nothing you can do to fix that." In this scenario however, the original site owner simply changed domain names. All of the old URLs would map perfectly to the new domain name:</p>

<blockquote>
  <p><strong>Old:</strong> http://example.com/blog/2007/04/post.html</p>
  
  <p><strong>New:</strong> http://example.org/blog/2007/04/post.html</p>
</blockquote>

<p>If I had stored the actual URLs, I could simply do a <a href="http://dev.mysql.com/doc/refman/5.0/en/replace.html">REPLACE in the database</a>. However, since I stored the Feedburner URLs in my database, it would be impossible for me to do so. That's because Feedburner obfuscates the actual URL data:</p>

<blockquote>
  <p><strong>Actual URL:</strong> http://example.com/blog/2007/04/post.html</p>
  
  <p><strong>Feedburner URL:</strong> http://feeds.feedburner.com/~r/exampleblog/epRL/~3/745213668/post.html</p>
</blockquote>

<p>I quickly built <a href="http://expandurl.com/">expandUrl</a> so that I could resolve any URL (be it a short url, tracking script, or feed redirect) to the actual URL. I then ran an automated script to determine the relative link on the old domain, replaced the old domain with the new domain, and updated the database accordingly. An hour or so later, and all those posts are now fixed.</p>

<p>I ran this script against the entire database of URLs to go ahead and resolve any other redirects found. I made an interesting discovery. Many of the RSS feed services will generate multiple redirect URLs for the same blog post permalink. My database had a UNIQUE index on the URL field as a means of preventing the same post from showing up multiple times, but it turned out the feed services were inadvertently circumventing that "protection."</p>

<p>In the end, integrating expandUrl has cleaned over 300 duplicate URLs from my database and helped improve the future maintainability of the link data the site has collected.</p>

<p>If you find expandUrl™ helpful, I'd love to hear about it! :)</p>
<hr/>Copyright &copy; 2010 <strong><a href="http://www.whoisgregg.com/blog">Gregg Hilferding</a></strong>. This feed is for personal non-commercial use only.]]></content:encoded>
			<wfw:commentRss>http://www.whoisgregg.com/blog/2009/04/introducing-expandurl-redirect-resolution-for-the-rest-of-us.html/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Guess a Number Between 1 and 100 in 7 or Fewer Tries</title>
		<link>http://www.whoisgregg.com/blog/2007/02/guess-a-number-between-1-and-100-in-7-or-fewer-tries.html</link>
		<comments>http://www.whoisgregg.com/blog/2007/02/guess-a-number-between-1-and-100-in-7-or-fewer-tries.html#comments</comments>
		<pubDate>Fri, 02 Feb 2007 21:09:38 +0000</pubDate>
		<dc:creator>Gregg Hilferding</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Signal]]></category>

		<guid isPermaLink="false">http://whoisgregg.com/blog/2007/02/guess-a-number-between-1-and-100-in-7-or-fewer-tries.html</guid>
		<description><![CDATA[A number is randomly chosen between 1 and 100. The "guesser" is told at each guess whether their guess is correct, too high, or too low.

Four methods tested with the sequence used to successfully guess the number "1."

Random Starts with a random number and draws a random guess from the range implied by the results [...]]]></description>
			<content:encoded><![CDATA[<p>A number is randomly chosen between 1 and 100. The "guesser" is told at each guess whether their guess is correct, too high, or too low.</p>

<p>Four methods tested with the sequence used to successfully guess the number "1."</p>

<p><strong>Random</strong> Starts with a random number and draws a random guess from the range implied by the results of each round.<br />
    <code>Guessing: 65, 41, 32, 18, 14, 9, 3, 1</code></p>

<p><strong>Equal Division</strong> Starts with 50 and each time guesses the median value from the range implied by the results of each round.<br />
    <code>Guessing: 50, 25, 13, 7, 4, 2, 1</code></p>

<p><strong>Fibonacci Division</strong> Starts with 50 and each time guesses the golden ratio value from the range implied by the results of each round. In other words, divides by 1.618033989 instead of by 2. So either 32 or 81 will be it's second round guess.<br />
    <code>Guessing: 50, 32, 21, 14, 10, 7, 5, 4, 3, 2, 1</code></p>

<p><strong>Fibonacci Offset Division</strong> Starts with 50 and each time guesses the golden ratio value based on whether they were too high or too low. So a guess of 50 that was "Too Low" would then choose 81. A guess of 50 that was "Too High" would choose 19.<br />
    <code>Guessing: 50, 19, 7, 3, 1</code></p>

<p>I expected that Equal Division would be the winner but wanted to play around with some other options just to see. Here's the distribution of how many tries it took to guess the correct number over 500 iterations using each of the different methods.</p>

<p>The condition for a win is guessing in fewer tries than the base-2 logarithm of the range (in this case the range is 100 and the base-2 logarithm of 100 is 6).</p>

<p><img id="image51" src="http://whoisgregg.com/blog/wp-content/uploads/2007/02/number-guesser-algorithm-charts.png" alt="Number Guesser Result Charts" /></p>
<hr/>Copyright &copy; 2010 <strong><a href="http://www.whoisgregg.com/blog">Gregg Hilferding</a></strong>. This feed is for personal non-commercial use only.]]></content:encoded>
			<wfw:commentRss>http://www.whoisgregg.com/blog/2007/02/guess-a-number-between-1-and-100-in-7-or-fewer-tries.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Multiple Transparent Borders</title>
		<link>http://www.whoisgregg.com/blog/2007/02/multiple-transparent-borders.html</link>
		<comments>http://www.whoisgregg.com/blog/2007/02/multiple-transparent-borders.html#comments</comments>
		<pubDate>Thu, 01 Feb 2007 20:01:49 +0000</pubDate>
		<dc:creator>Gregg Hilferding</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Signal]]></category>

		<guid isPermaLink="false">http://whoisgregg.com/blog/2007/02/multiple-transparent-borders.html</guid>
		<description><![CDATA[

When drawing a text string that has multiple text borders like, for example, the family reunion t-shirt design here, you have unique issues building the multiple layers of borders.

Depending on how you draw multiple borders you can get many different effects:

What's the difference between these different approaches?



Answers

In option C, each letter is individually drawn starting [...]]]></description>
			<content:encoded><![CDATA[<p><img id="image47" src="http://whoisgregg.com/blog/wp-content/uploads/2007/02/multiple-borders-example.png" alt="Multiple Borders Example" class="fr" /></p>

<p>When drawing a text string that has multiple text borders like, for example, the <a href="http://www.getyourshirts.com/t-shirts/reunion/sp412.html">family reunion t-shirt design here</a>, you have unique issues building the multiple layers of borders.</p>

<p>Depending on how you draw multiple borders you can get many different effects:</p>

<h2>What's the difference between these different approaches?</h2>

<p><img id="image50" src="http://whoisgregg.com/blog/wp-content/uploads/2007/02/multiple-transparent-border-test.png" alt="Multiple Border Tests" /></p>

<h2>Answers</h2>

<p>In option C, each letter is individually drawn starting at the largest border then working inwards to the fill. When drawing arced text, each letter is drawn in a different position so you have to draw each letter separately.</p>

<p>In option B, each border is drawn with a transparent fill then the separate layers are flattened.</p>

<p>In option A, the entire string is drawn starting at the largest border then working inwards to the fill.</p>
<hr/>Copyright &copy; 2010 <strong><a href="http://www.whoisgregg.com/blog">Gregg Hilferding</a></strong>. This feed is for personal non-commercial use only.]]></content:encoded>
			<wfw:commentRss>http://www.whoisgregg.com/blog/2007/02/multiple-transparent-borders.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>301 Redirects With a Custom 404 Page</title>
		<link>http://www.whoisgregg.com/blog/2006/11/301-redirects-with-a-custom-404-page.html</link>
		<comments>http://www.whoisgregg.com/blog/2006/11/301-redirects-with-a-custom-404-page.html#comments</comments>
		<pubDate>Fri, 24 Nov 2006 19:10:27 +0000</pubDate>
		<dc:creator>Gregg Hilferding</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[SEO]]></category>
		<category><![CDATA[Signal]]></category>
		<category><![CDATA[Webmaster]]></category>

		<guid isPermaLink="false">http://whoisgregg.com/blog/2006/11/301-redirects-with-a-custom-404-page.html</guid>
		<description><![CDATA[For anyone who cares about search engine rankings and has had to move a page or domain from one location to another, the handling of redirects is very important. The wrong type of redirect and any links pointing to the old location don't count towards the contents new location.

If you read most work on "How [...]]]></description>
			<content:encoded><![CDATA[<p>For anyone who cares about search engine rankings and has had to move a page or domain from one location to another, the handling of redirects is very important. The wrong type of redirect and any links pointing to the old location don't count towards the contents new location.</p>

<p>If you read most work on "<a href="http://www.webmasterworld.com/forum92/82.htm">How To Set Up a 301 Redirect</a>" it involves delving into Apache .htaccess files or <a href="http://www.webmasterworld.com/forum47/3287.htm">waving a dead rat over a .NET server</a> while clucking like a chicken. In those odd shared hosting cases where you have no access to modify .htaccess files you may be further screwed into learning about meta redirects and determing just <a href="http://help.yahoo.com/help/us/ysearch/slurp/slurp-11.html">how Yahoo! is going to interpret a 2 second delay versus a 3 second delay</a>.</p>

<p>Here's a method for handling redirects in a way that is simple to manage, requires only PHP and the ability to define a custom error page, and allows you to prepare your redirects <em>before</em> you move the page with a seamless redirect once you have. Even with the ability to control every aspect of my Apache server, I still prefer this method.</p>

<h3>How it works</h3>

<p>When you move a page to a new location the most important thing is to ensure a user or search engine spider does not receive a "File Not Found" error (hereafter referred to as a "404"). It's important to understand how a request for a non-existant page is handled by the server. Here's a diagram I put together:</p>

<p><img id="image9" src="http://whoisgregg.com/blog/wp-content/uploads/2006/10/apache-handles-404.gif" alt="How Apache Handles a Request for a Non-Existant Page" /></p>

<p>If you didn't realize already, it's a simplified diagram of the process. Where in this process do you intercede to prevent a 404 error? Because everything a server must do takes up processing power, it is logical to conserve server resources and intercede as soon as possible. For example, if you could go out and fix all your visitor's bookmarks and search engine listings to point to the new page, you should. Then, no one even would have to worry about 404 pages or dealing with redirects! :)</p>

<p>So, logically, to conserve server resources, the next step in which we can intercede is the checking of the .htaccess file. (For simplicities sake, I won't discuss editing the httpd.conf to place the redirects in the VirtualHost section.) For example, we could put something like this in our .htaccess file:</p>

<blockquote class="code">
RedirectMatch 301 /olddirectory/([^.]+)\.html$ http://www.example.com/newdirectory/$1.html
</blockquote>

<p>And, that code would work, just as expected! But, once you <em>and your boss and clients</em> learn about the wonder of the 301 redirect, some of the arguments against changing page locations willy nilly will go away and you'll find yourself with .htaccess directives that are dozens of lines long. Which reminds us that <em>Apache parses all the directives it finds</em> for <em>every page request</em> it receives in <em>every directory in which that page may be found</em>. Suddenly our performance savings by using Apache to handle redirects are being whittled away by an excess number of directives.</p>

<h3>The solution lies downstream</h3>

<p>Scroll back up and bit and take a look at that diagram. When a page isn't found, the web server will serve up an error page. It will even serve a custom error page if configured to do so. How do you configure it do so? It's easy and I'm going to skip ahead a bit and reveal that our custom error page will be a PHP script so I can provide all the .htaccess code you'll need in this one bit of code:</p>

<blockquote class="code">
ErrorDocument 404 /404.php
</blockquote>

<p>That line added to the main .htaccess file for your entire script is the only and last time you'll need to edit .htaccess to deal with your 301 redirects. All the work occurs in that PHP script.</p>

<h3>PHP headers overrule Apache headers</h3>

<p>Even though Apache will normally serve a 404 header along with your custom 404 page, you can easily overrule that with a <a href="http://php.net/header">PHP header</a>. So, why note send a 301 header? Once we make this cognitive leap, it's dead simple to put all your redirect code into your custom 404 page and check the request against your URL patterns.</p>

<p>Using a database structure like so:</p>

<blockquote class="code">
CREATE TABLE `redirects` (
  `primary` bigint(20) unsigned NOT NULL auto_increment,
  `old_uri` varchar(254) NOT NULL default '',
  `new_uri` varchar(254) default NULL,
  `action_type` mediumint(9) default NULL,
  `domain_match` varchar(254) NOT NULL default '',
  PRIMARY KEY  (`primary`),
  KEY `old_uri` (`old_uri`,`new_uri`,`action_type`,`domain_match`),
  FULLTEXT KEY `old_uri_2` (`old_uri`)
) ENGINE=MyISAM;
</blockquote>

<p>You can use the following PHP code at the beginning of your custom 404 page:</p>

<blockquote class="code">
&lt;?php

// include your own DB connection info here
$link = mysql_connect('localhost', 'mysql_user', 'mysql_password');

// build a complete URL from server variables
$complete_request = 'http'.(($_SERVER['HTTPS']=='on')?'s':'').'://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];

// Parse out the URI
$parsed_request = parse_url($complete_request);

// Especially badly formed URLs will throw a false from the parse_url function, we only continue if that is not the case
if($parsed_request !== false){

    // Check the DB for a specific entry
    $query = sprintf("SELECT * FROM `redirects` WHERE `domain_match` LIKE '%s' AND `old_uri` LIKE '%s' LIMIT 1",
               ''.mysql_real_escape_string($_SERVER['SERVER_NAME']).'',
               ''.mysql_real_escape_string($parsed_request['path']).'%'
    );
    //print($query); // for debugging

    $result = mysql_query($query); 
    while ($row = mysql_fetch_array($result, MYSQL_ASSOC)) {
        $redirects_found[] = $row;
    }
    // print_r($redirects_found); // for debugging

    // If there is a match, redirect the user
    if(count($redirects_found)==1){

        // send the right type of redirect
        if($redirects_found[0]['action_type'] == '302') {
            header('HTTP/1.1 302 Moved Temporarily'); 
            header('Location: http'.$s.'://'.$_SERVER['HTTP_HOST'].$redirects_found[0]['new_uri'].'');
            $headers_sent = true;
        } else {
            header('HTTP/1.1 301 Moved Permanently'); 
            header('Location: http'.$s.'://'.$_SERVER['HTTP_HOST'].$redirects_found[0]['new_uri'].''); 
            $headers_sent = true;
        }
        // You can add additional behaviors for other header codes here

    } // if(count($redirects_found)==1){

} // if($parsed_request !== false){

// Exit if we have sent headers
if ($headers_sent) { 
     die;
}

// Otherwise, include friendly 404 HTML below :)
?&gt;
</blockquote>

<p>With this code in place, you can add entries to the <code>redirects</code> table ahead of removing/moving pages. As soon as your new pages are in place, just rename or delete the old files and all incoming requests (from both users and spiders) will automagically be redirected to the new location.</p>
<hr/>Copyright &copy; 2010 <strong><a href="http://www.whoisgregg.com/blog">Gregg Hilferding</a></strong>. This feed is for personal non-commercial use only.]]></content:encoded>
			<wfw:commentRss>http://www.whoisgregg.com/blog/2006/11/301-redirects-with-a-custom-404-page.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Wordpress mod_rewrite Still Allows Old URL String</title>
		<link>http://www.whoisgregg.com/blog/2006/10/wordpress-mod_rewrite-still-allows-old-url-string.html</link>
		<comments>http://www.whoisgregg.com/blog/2006/10/wordpress-mod_rewrite-still-allows-old-url-string.html#comments</comments>
		<pubDate>Fri, 27 Oct 2006 15:52:15 +0000</pubDate>
		<dc:creator>Gregg Hilferding</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[SEO]]></category>
		<category><![CDATA[Webmaster]]></category>

		<guid isPermaLink="false">http://whoisgregg.com/blog/2006/10/wordpress-mod_rewrite-still-allows-old-url-string.html</guid>
		<description><![CDATA[If you migrated a Wordpress blog from the old style URLs using the built-in permalink options, you may be surprised that all the old URLs are still accessible. This causes a problem with search engines which have already indexed those old URLs as they will look at the new URLs as duplicate content.

/blog/?p=123

The "quick fix" [...]]]></description>
			<content:encoded><![CDATA[<p>If you migrated a Wordpress blog from the old style URLs using the built-in permalink options, you may be surprised that all the old URLs are still accessible. This causes a problem with search engines which have already indexed those old URLs as they will look at the new URLs as duplicate content.</p>

<p><code>/blog/?p=123</code></p>

<p>The "quick fix" for this is a few lines of code added to the top of the "single.php" file in your wordpress template:</p>

<p><code>
&lt;?php
if(preg_match("/p=[0-9]+/", $_SERVER['QUERY_STRING'])){
    header("HTTP/1.1 301 Moved Permanently");
    header("Location: ".get_permalink());
}
?&gt;
</code></p>

<p>I will be writing up a plugin to add this functionality in a more Wordpress friendly way, but for now a quick code change will work. :)</p>
<hr/>Copyright &copy; 2010 <strong><a href="http://www.whoisgregg.com/blog">Gregg Hilferding</a></strong>. This feed is for personal non-commercial use only.]]></content:encoded>
			<wfw:commentRss>http://www.whoisgregg.com/blog/2006/10/wordpress-mod_rewrite-still-allows-old-url-string.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
