Author Biography

Ryan Barr Ryan Barr is a young web standards designer from Colorado. For eight years he has been developing and experimenting with powerful and valid code. Read more about him...

Show Respect

Friendly Partners

  • Scriptplayground
  • (mt) Media Temple

Creating Friendly URLs with PHP and .htaccess

Sunday, November 2nd, 2008

One of the most desired effects can be the hardest to accomplish correctly. Here is my stick in the pile.

Unaware of what a Friendly URL is? It turns a dynamic URL into something a bit more static and readable by a human being. For example:

http://yourdomain.com/index.php?page=blog&post=1

Would be turned into:

http://yourdomain.com/blog/post-title/

Making your URLs friendly allows for a "permanent link" to be created (meaning that it's unlikely to change and people can trust that link or bookmark will be good for a very long time), allows for it to easily be deciphered by human eyes and understood, and allows for search engines to index them more properly. It also creates the illusion that it is truly a separate file, which will create for more search engine results in the long run.

"How is it done?" is running through your head, I know it. So, let's begin:

It is highly recommended that you have at least some basic PHP experience before proceeding with this tutorial.

There are hundreds of approaches to get the same effect. I've pieced together numerous approaches and different styles to get a pretty stable effect: the ability to manually set directories with one single variable. Technically, it's all that is really needed here. No person should be running all of their code off of index.php; I believe code should be organized.

In this tutorial, we are going to be going off a similar style to what I use here at Spookyismy.name. We will create a few "subfolders" (which will actually be files), and have a few of them dynamic. Let's begin with the .htaccess end of things.

If you are running on a Windows setup you will not be able to use this .htaccess method. If I find a suitable alternative, I will be sure to update this tutorial.

Lets begin by creating a .htaccess file. Simply open your favorite text editor, copy the following code in, and save it as .htaccess. Do note, though, that it will treat this as a "hidden file" in case you lose it. Uploading this to a folder on a server will change the rules for that folder.

Options +FollowSymLinks
RewriteEngine on
RewriteRule ^blog/?$ blog.php
RewriteRule ^blog/([-a-zA-Z0-9_]+)/?$ blog.php?name=$1 [QSA]

RewriteRule ^tutorials/?$ tutorials.php
RewriteRule ^tutorials/([-a-zA-Z0-9_]+)/?$ tutorials.php?name=$1 [QSA]

RewriteRule ^about/?$ about.php

RewriteRule ^contact/?$ contact.php

For the curious ones, let's decipher this as best we can. We begin by turning on the RewriteEngine, saying to listen to any new rules we are going to set. Then, for each directory illusion we want to create, we set a rewrite rule such as RewriteRule ^blog/?$ blog.php. Then, for the "subfolders" we are hoping to have variables, such as blog and tutorials, we set that any trailing "subfolder" may have any assortment of characters or numbers, and may or may not have a trailing slash. Then, the [QSA] says that a query string might be present.

By having the .htaccess file above on your server, it will make for the effect that blog.php can be accessed just by having /blog/. Pretty cool, right? Well, then we have the second half. If we type in /blog/first-post/ the URL would actually be read by your PHP as blog.php?name=first-post. So, yes, that means you can call the variable $_GET['name'].

So far fantastic. If you are just looking to have a static website with all the files with static content, you can stop where you're at. Although for most of us, we are looking for a bit more dynamic approach. This is where our PHP will come in. We can access the information in the URL simply by calling the variable we have set, if it is set. For example (using the variable from the .htaccess example: name):

<?php

if (isset($_GET['name'])) {

// Insert MySQL string here that would pull where entry has a "safe name" of $_GET['name']

} else {

echo "This is the main page.";

}

?>

From our first example, we assume that the files blog.php, tutorials.php, about.php, contact.php are present. Each of these files will need a similar script as to what we have in the second example. The second example (above) checks to see if the $_GET['name'] variable is present. If it is, then we are going to want to check our database for the presence of that variable, however we want it set*. If it isn't set, then we are going to include the main file.

For example, if we have /blog/first-post/, our script will see that the $_GET['name'] variable is present and attempt to see if there is somewhere in the database (depending on your code) that has "first-post". If we have /blog/, our script will see that the variable is not present and echo that we are at the main blog page.

*Each person's needs are a bit different. If you notice in the comment present in the second example, it says it is checking for a "safe name". These are often post titles that are in all lowercase with all special characters removed, and spaces replaced with "-". You can see that the URL for this particular tutorial has a "safe name". You can feel free to use a unique ID number and check your database for an ID instead, it is all your preference. Do remember, though, that search engines do tend to like "safe names" more though.

.htaccess and friendly URLs are very confusing to many people, I understand that. There are many people who will go from tutorial to tutorial trying to find a good solution. I can tell you that it is doubtful you will find some code you can copy and paste to suit your needs, especially some code that will work. If you need any help, please, PLEASE, feel free to contact me at any time.

I would like to mention that I obviously did not come across this knowledge on my own. Along with tons of Google searching and piecing code together, I got quite a bit of help from Matthew Keefe and Miles Johnson. Thanks, guys!