Url Routing with PHP – Part One

Posted by:

Most PHP frameworks use some variation of the front controller pattern to centralize common code and logic. There are advantages and disadvantages to this. I am going to ignore those for now. In fact the first part of this series will explore a simple procedural URL routing method that contains many of the disadvantages. In later articles we will build upon this basis and address the disadvantages.


  • PHP
  • Apache with mod_rewrite enabled.

Redirecting with mod_rewrite

This article doesn’t discuss mod_rewrite and the .htaccess in detail, the following .htaccess file is sufficient for the needs of our simple front controller. The .htaccess file should be placed in the same directory as the front controller script. All requests will be sent to index.php while requests for a file or directory that does exist will bypass the mod_rewrite rules and be served directly by the web server.

file: .htaccess

  1. Options +FollowSymLinks
  2. IndexIgnore */*
  3. # Turn on the RewriteEngine
  4. RewriteEngine On
  5. #  Rules
  6. RewriteCond %{REQUEST_FILENAME} !-f
  7. RewriteCond %{REQUEST_FILENAME} !-d
  8. RewriteRule . index.php

The Entry Point

I chose a simple format for the incoming URL, a command followed by its parameters separated by forward slashes “/”.

For example:

  1. $requestURI = explode(‘/’, $_SERVER[‘REQUEST_URI’]);

in this example $requestURI would contain.

  1. Array ( [0] => [1] => command [2] => parameter1 [3] => parameter2 [4] => )

This works fine except in the case where the front controller is not in the root directory.

For example:

in this case $requestURI would contain.

  1. Array ( [0] => [1] => myapps [2] => app1 [3] => command [4] => parameter1 [5] => parameter2  [6] => )

To filter out the path to the front controller we will need to use the $_SERVER['SCRIPT_NAME'] variable.

  1. $requestURI = explode(‘/’, $_SERVER[‘REQUEST_URI’]);
  2. $scriptName = explode(‘/’,$_SERVER[‘SCRIPT_NAME’]);

In the above example $scriptName would contain.

Array ( [0] => [1] => myapps[2] => app1 [3] => index.php )

Which can be used to remove the path and script name from the URI.
Note: As you can see the explode method leaves an empty array member at the first position. This will be fixed by the following code.

  1. $requestURI = explode(‘/’, $_SERVER[‘REQUEST_URI’]);
  2. $scriptName = explode(‘/’,$_SERVER[‘SCRIPT_NAME’]);
  4. for($i= 0;$i < sizeof($scriptName);$i++)
  5.         {
  6.       if ($requestURI[$i]     == $scriptName[$i])
  7.               {
  8.                 unset($requestURI[$i]);
  9.             }
  10.       }
  12. $command = array_values($requestURI);

After the code above $command would contain.

Array ( [0] => command [1] => parameter1 [2] => parameter2  [3] => )

Dispatching the command

Now that we have a command with its parameters stored in an array it is trivial to handle them via a switch statement. The following is a short example.

  1. switch($command[0])
  2.       {
  4.       case ‘commandOne’ :
  5.                 echo ‘You entered command: ‘.$command[0];
  6.                 break;
  8.       case ‘commandTwo’ :
  9.                 echo ‘You entered command: ‘.$command[0];
  10.                 break;
  12.       default:
  13.                 echo ‘That command does not exist.’;
  14.                   break;
  15.       }

Try it out

You can visit to see it in action and download the sample code.

Next up

The next part in the series will focus on moving the URL router into a class and out of the global namespace.

Update I’ve updated the .htaccess per the comment made by DrBacchus.


About the Author


  1. Tim  April 2, 2007

    that's sweet ! Can't wait to read part-2

  2. Diego Anzlin  April 2, 2007

    Oi Friend,

    for script to function correctly is necessary to add the following line before "FOR"

    — LINE TO ADD —-
    $scriptNameArray = array_values($scriptName);
    — LINE TO ADD —-

    for($i= 0; $i

  3. Doug Hill  April 2, 2007

    Copy and paste seems to have bit me! I've fixed the code now Diego. Thanks for the catch.

  4. Matthew Turland  April 6, 2007

    For users of PHP 4.3.0+ I believe lines 4-10 in the second source code example can be simplified by making use of the array_diff_assoc function like so:

    $command = array_diff_assoc($requestURI, $scriptName);

    Untested, so I could be wrong, but just a thought. Nice tutorial!

  5. Doug Hill  April 7, 2007

    array_diff_assoc() may work I haven't tried it either. I picked the longer for loop to make what was happening in the sample code clearer. I'll try it out though and see.

    $commandArray = array_diff_assoc($requestURI,$scriptName);
    $commandArray = array_values($commandArray);

    The above works fine. The article isn't really about how you parse the URLs though, off hand I can think of dozens of schemes. The point is actually being able to translate some arbitrary format into a command array.. or in later articles a command object.

  6. DrBacchus  April 23, 2007

    Your rewrite conditions are slightly inaccurate:

    RewriteCond %{REQUEST_FILENAME} !-f [OR]
    RewriteCond %{REQUEST_FILENAME} !-d

    Because this is an OR condition, the rewrite rule will always be run. (A OR B) is TRUE when either A or B is true. Any argument will always be either (not a file) or (not a directory). You need to remove the [OR] there, otherwise a request for index.php will cause a loop.

    What you want is "if it's not a file AND it's not a directory":

    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d

  7. Doug Hill  April 23, 2007

    You are correct, I need to edit the article, in later versions of the sample code I removed the [OR].

  8. Peter Mansen  June 2, 2007

    How do you use mod_rewrite on the sam server? Thank you!

  9. IndridCold  July 16, 2007

    Why not use something simple like:

    substr($_SERVER['REQUEST_URI'], strlen($_SERVER['SCRIPT_NAME']));

    to distinguish between the script name and the parameters, then explode on that?

    Just an idea.

  10. IndridCold  July 16, 2007

    You might also want to filter out multiple slashes in anticipation of exploding on the '/' as your separator.

    $rawParams = preg_replace('@[\/]{2,}@', '/', $rawParams);

  11. Jesús Flores  September 11, 2007

    It's simply awesome ! :) Ty

  12. What is a URL?  November 23, 2007

    What is the advantage to doing all this? Why not have the PHP script parse the path instead of Apache? Why learn two (possibly conflicting) languages?

  13. Doug Hill  November 24, 2007

    I'm not sure I understand your question, The point of the artice is to have the PHP script parse the path.

  14. Peter  December 18, 2007

    re: What is the advantage to doing all this?

    If you're referring to the usage of mod_rewrite, the point is (IMHO) to hide index.php from the URL so that instead of or you can use

    If you're looking for motivation, read

  15. Rauli  December 30, 2007

    With this method, is it possible to get error404 message at all for non-existent 'pages'?

  16. Alessandro Motta  March 21, 2008

    nice tutorial you wrote here. I built a similar class for PHP, that works fine. But now, there's a problem:

    I'd like to route urls like the MediaWiki uses. That means urls like http://localhost/user:Test/param1/param2 But when I call these addresses i receive the message (from Apache) that the access has been denied. Do you know any solutions to this problem?

    Thanks for the tutorial and greetings

  17. SMartcoder  March 26, 2008

    Nice and brilliant script.
    So simple.

    ANother helpful url is found here.

    Routing in php
    Php routing

  18. jay  April 16, 2008

    Hi guys,

    I'm newbie to all this stuff. I am wondering if i could do the following WITHOUT CHANGING THE URL
    to call"bar"

    Thanks in advance :)

  19. Vitou  June 10, 2008

    Hi all,
    I am wonder whether your Rewrite Rull works with Apache on Window plateform or not.

  20. Donatello  June 13, 2008

    Here's My Version!


    RewriteEngine on
    RewriteCond $1 !^(index\.php|img|css|js)
    RewriteRule ^(.*)$ /index.php/$1 [L]


    abstract class URI {

    protected $pathArray = array();

    protected $path;

    public function __construct() {

    global $HTTP_SERVER_VARS;

    $this->pathArray = explode("/",$HTTP_SERVER_VARS["REQUEST_URI"]);

    public function getPathByNumber($pathNumber){

    if (substr($this->pathArray[1], -4, 4) == '.php') {
    $this->path = mysql_escape_string($this->pathArray[$pathNumber + 1]);
    }else {
    $this->path = mysql_escape_string($this->pathArray[$pathNumber]);

    return $this->path;

    } //ends URI class

  21. Cakka  July 29, 2008

    Thanks for your tutorial.

  22. lewen7er9  December 16, 2008

    Thank you for the tutorial. FYI, the link to sample code for part 1 is broken (getting a 500 error).

  23. SiCEramiSip  December 18, 2008


    As a fresh user i only wanted to say hello to everyone else who uses this site :>

  24. Peter  February 8, 2009

    What about to mention something about RewriteBase when files not in root dir!
    Example does not have source code for downloading like it is promised there.

  25. vtec sohc  April 5, 2009

    This is really helpful, nice code, nice technique, awesome!!!

    Thanks a lot mate!

  26. rudak  April 11, 2009

    The line 6 in .htaccess must be
    RewriteCond %{REQUEST_FILENAME} !-f[AND]

  27. WB  April 16, 2009

    I setup the patter and it works great except for on gliche… I was wondering if anyone else has and issue with image references when an extra slash is added to the URI. i.e. If the url is… – images are referenced fine as…

    however if – broken reference as…

    Anyone have any thoughts on where to look?

  28. boris  April 29, 2009

    @WB I think you shoudl use tirm('/',$url)…

  29. Umer  June 3, 2009

    I am new user.
    I want to redirect

    I have tried this through .htaccess but CSS and connection breaks.

    Is there any other way to do this.

    Thanx in Advance.

  30. Jove Sharma  July 1, 2009

    Hi there,

    do someone have any code for htccess or php so that i can redirect my own made extension file to the respective php file, but the browser shows the extension by me.

    like if i type a url link

    but internally it will display the content of

    while showing the in the browser window.

  31. Neuromancer  August 22, 2009

    It doesn't work.

    $requestURI is empty.

  32. Erik  September 13, 2009

    Hi. Thanks for this Tutorial. I enjoyed it.

    Just a minor note on all these attempts to get command and parameters by comparing paths – that's not necessary if you store the request in a $_GET parameter like this:
    <samp>RewriteRule (.*) index.php?r=$ [L,QSA]</samp>
    This way you can drop all the string comparing and extract all parameters with one <samp>explode('/',$_GET['r']);</samp>

  33. Matthew Fedak  October 22, 2009

    Thanks for this Doug, its a simple post and suppose people have there own ways in which they get round the problem, but it explains the concept to wanabe php developers really well. Quick point Eric (last commentor) point is good but can i would advise newbies to avoid.

  34. sethhhh  February 7, 2010


    I have a site made with symfony 1.1 and i whant to change it with
    another site made with 1.2.
    my proplem is that i whant to change some URL…
    For example i have a url thath looks like this on my old site:

    On my new site it will be:

    How can i get the old url if google sends someone on it…and change
    it in my new one?

    Thank you!

  35. Kunal Rajendra Sagar  September 21, 2010

    Thanks simple and useful…

  36. Tom  October 11, 2010

    try this one:

    probably the simplest explanation

  37. Allison  May 20, 2011

    Works great, thank you

  38. taxi bucuresti  September 19, 2011

    Very nice tutorial. I always tryed to imagine how the frameworks achieve nice url interpreting/dispatching.
    I have a question though, at an URL like the one of this page ( in order for you to choose the correct article do you search for an item with a title matching the parameter or you are using a rewrite condition.
    I saw some sites using urls like:
    I presume that the 179 param is the article id and the rest is just for SEO purposes.
    Can you please tell me what you know about this matter? Or point me to another resource?

  39. Bruno Cassol  October 1, 2011

    Thank you for sharing! Quick and easy.

  40. Josh Ferrara  October 11, 2011

    Hi Doug,

    I was wondering if I could possibly obtain the code for part one? The download link seems to be missing.

    Thanks so much! I realize this is an old post, I appreciate your time!

  41. Doug Hill  October 16, 2011

    I know the article said there was a download but apparently I misplaced it myself. It has been a while but I think it was simply the code as shown in the article all in one file. Give me a few days and I will get something up there.

  42. Mufid  December 29, 2011

    Great! Thanks. I just know how to make use of REQUEST_URI variable.

    Btw, where's the part two?

  43. wuttdan  April 4, 2012

    nice and like it.

  44. wuttdan  April 5, 2012

    Help me please
    I use this route (.htaccess), But when is use my css not effect.
    /**** this is my .htaccess
    Options +FollowSymLinks
    IndexIgnore */*

    # Turn on the RewriteEngine
    RewriteEngine On

    # Rules
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d

    # otherwise forward it to index.php
    RewriteRule . index.php
    #RewriteRule .* – [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]

  45. Tim  June 15, 2012

    Awesome, Thanks.

    I extended this by writing a class to handle aliases (changed page titles) that recognizes the old titles but still shows the correct content. It adds the canonical tag to aliases. Aliases are stored in a database table when the page title is changed.

    Another handy addition was to add a default page (404) for pages not found.

    I searched top and bottom for a simple script like this. Great job!

  46. godie  June 16, 2012

    so great thanks that its exactly I looking.

  47. Dan  June 25, 2012

    Not RESTful. Me no likey.

  48. Akki  July 12, 2012

    I couldnot find the files to download the complete source code. please share the link.

  49. Lio  September 12, 2012

    Hi guys.

    Where is the link to download of this example ?
    I go to
    but have no link here.


  50. Sell Car Quickly  February 27, 2014

    We stumbled over here coming from a different website and thought I may as
    well check things out. I like what I see so i am just following you.
    Look forward to looking at your web page for a second time.

  51. teeth whitening enamel  March 4, 2014

    I do not even understand how I finished up here, but I
    thought this put up used to be great. I don't recognize who you are but certainly you are going to a famous blogger for those who are
    not already. Cheers!

  52. Battle Command Hack Android  March 6, 2014

    I really like your blog.. very nice colors & theme. Did you create
    this website yourself or did you hire someone to
    do it for you? Plz answer back as I'm looking to
    construct my own blog and would like to find out where u
    got this from. kudos

  53. SEO Services  March 10, 2014

    It's a shame you don't have a donate button! I'd most certainly donate to
    this superb blog! I guess for now i'll settle for book-marking and adding your RSS feed
    to my Google account. I look forward to new updates and will talk about
    this blog with my Facebook group. Chat soon! – SEO Services.

  54. Light  March 16, 2014

    The same as English, you only need to have an understanding of a few simple actions-word conjugation models.
    Cruise over the Golfe nufactured la Napoule
    for the harbor of La Napoule. It is the largest people body part and it's the
    particular groups primary line of safety from contamination, temps,
    illness and detrimental chemicals.

  55. ???????  April 21, 2014

    Awesome! Its truly amazing article, I have got much clear idea on the topic of
    from this article.

  56.  May 9, 2014

    Hi this is somewhat of off topic but I was wondering if blogs use WYSIWYG editors or if you
    have to manually code with HTML. I'm starting a blog soon but have no coding experience so I wanted to
    get guidance from someone with experience. Any help would be greatly appreciated!

  57. freaky-monster schlüsselanhänger  June 1, 2014

    Hey I know this is off topic but I was wondering if you knew of
    any widgets I could add to my blog that automatically tweet my newest twitter updates.
    I've been looking for a plug-in like this for quite some time and was
    hoping maybe you would have some experience with something like this.
    Please let me know if you run into anything. I truly
    enjoy reading your blog and I look forward to your new updates.

  58. taxi seguro  June 9, 2014

    It's very straightforward to find out any topic on web as compared to
    books, as I found this piece of writing at this web site.

  59. wholesale jerseys Timberwolves cheap  June 10, 2014

    wholesale cheap Browns jerseys Review China

  60. cheap Kings jerseys from china  June 13, 2014

    wholesale cheap Lakers jerseys china. 90% Off and 100% Stitched.

  61. cheap chinese Magic jerseys  July 3, 2014

    Buy Safe authentic Knicks jersey Center Reviews

  62. copysniper  July 12, 2014

    It is really a nice and helpful piece of info. I am satisfied that
    you simply shared this useful information with us.
    Please stay us informed like this. Thank you for sharing.

  63. scaffolding  July 25, 2014

    Wow that was unusual. I just wrote an really long comment but
    after I clicked submit my comment didn't show
    up. Grrrr… well I'm not writing all that over
    again. Anyhow, just wanted to say excellent blog!

  64. clothes  August 6, 2014

    I am really impressed along with your writing talents as smartly as with thee structure on your weblog.
    Is that this a pzid theme or did you modify it yourself?
    Either way stay up the nice high quality writing, it is uncommon to see a nice
    weblog like this one nowadays..

  65. footwear  August 6, 2014

    What's up everyone,it's my first go to see at this site, and piece of writing is
    in fact fruitful designed for me, keep up posting these content.

  66. Bill  August 13, 2014

    This is very helpful. Please put a link to part two in the body of your post somewhere. Thank you.

  67. top pool cleaners 2014  August 18, 2014

    Hi, I think your blog might be having browser compatibility issues.
    When I look at your blog site in Safari, it looks
    fine but when opening in Internet Explorer, it has some overlapping.
    I just wanted to give you a quick heads up! Other then that, awesome blog!

  68. ????????????? ??  September 2, 2014

    Hi! Do you know if they make any plugins to protect against hackers?
    I'm kinda paranoid about losing everything I've worked hard on. Any tips?

Add a Comment