Apache mod_rewrite? Pah! Rewriting URLs without using mod_rewrite

Written by Richard, on 23rd June 2021

If there's anything that strikes fear into many-a-developers heart and turns it to stone, it's having to face the prospect of writing mod_rewrite Apache directives. Actually, it may just be me, but I really don't like it.

So why do it at all? I've just converted the RGraph website to use a different way of doing things that is far more straightforward and easier to handle, particularly if you're comfortable with a server-side scripting language like PHP.

I've been using PHP now for over 20 years and whilst I'm not fully au-fait on recent versions or additions to the language (the last version I used as a fully-paid-up server-side developer was around the version 5.2 era) I'm still very confident with it when you compare it with something like, say, Apache configuration. Particularly my hosting company's version of it which I'm adamant is not a standard implementation.

So imagine my sheer delight when I had the random thought (which I presume others might have had long, long ago) of handling rewrites with PHP. Oh, how wonderful that would be. So that's what I've done with the RGraph website and oh how well it works! It's so much easier to work with from day to day and has the potential to save a lot of time and heartache.

How does it work?

It's very simple to implement and involves a small snippet of Apache configuration (which is not that difficult to understand) and a PHP script (which can be as simple or as complex as you wish).

This small snippet of Apache configuration can go in a .htaccess file and, in my case, goes in the .htaccess file in each directory (I only have a small number of directories on the website). This example is from the .htaccess file which is located in the /blog directory.

RewriteEngine On
RewriteBase /blog
RewriteCond %{REQUEST_URI} !-d
RewriteCond %{REQUEST_FILENAME} !\.(png|gif|jpg|jpeg|css|js|ttf|xml|bz2|gz|tar|zip|csv)$
RewriteRule  .* _router.php [L]

Now, breaking that down, the first line turns on mod_rewrite, the second line (which may or may not be required - I'm sure that I've not used it in the past) sets the directory (this file is located in the /blog directory remember) and the third, fourth and fifth lines are the rewrite rules which send everything to the _router.php PHP script. Let's break down that rule (RewriteRule .+ _router.php [L]):

The RewriteRule is the name of the directive, the .+ matches the filename (one or more characters in this case - so any filename) and the _router.php is the name of a PHP script (located in the /blog directory) that handles the job of looking at the request and deciding what to do with it. the [L] flag tells mod_rewrite that this is the last rule. It's not really needed in this case as there's nothing else in the .htaccess file.

So what happens then? Well, in the _router.php script, it's a simple matter of either redirecting to a different URL or, if you want to keep the URL in the navigation bar, show the correct content. So you can:

  1. Redirect by sending the relevant HTTP headers like this:
    http_response_code(301);                        // Send a permanent redirect response code
    header('Location: ' . '/some/where/else.html'); // Send the new location header
    exit;                                           // End the PHP script
    
  2. Show the relevant content. In this case, if you want to keep the URL in the address bar, you can use the require or include PHP functions to show the correct content like this:
    $file = basename($_SERVER['REQUEST_URI']);
    
    if ($file === 'authors') {
        require('authors.html');
        exit;
    }
    
    Or alternatively if the file exists on your filesystem you could just show it:
    $file = basename($_SERVER['REQUEST_URI']);
    
    if (file_exists($file) {
        require($file);
        exit;
    }
    

What happens with subdirectories?

If you have a .htaccess file and a _router.php in the /blog directory (for example) then due to the way that .htaccess files are handled by Apache, the /blog/.htaccess file will be checked even when you're requesting a file in a subdirectory.

So if you request the file /blog/foo/bar.html in your browser then the /blog/.htaccess will be checked and the /blog/_router.php file (or whatever you may have called it) will be called. In that case, the $_SERVER['REQUEST_URI'] PHP variable will contain the full address of the page, which in this example is /blog/foo/bar.html.

What if I've turned off override files?

If you've turned off override files (.htaccess is an override file) then this method provides an extra advantage. Normally, editing the server configuration files requires you to restart the server for any changes to be picked up but with this method, you wouldn't be editing the server configuration files to add a redirect/rewrite. You would add the above configuration once and then to add a new redirect/rewrite or to modify an existing one you can just change the _router.php file and the new change is effective immediately.

Performance considerations

Unless your server is experiencing meltdown levels of traffic then you're unlikely to notice any difference in performance. The extra versatility that this method brings (especially if you're comfortable with PHP) compared to having to struggle with Apache configuration and mod_rewrite directives is well worth it in my opinion.