# Using .htaccess Files for Pretty URLs

Continuing our review of .htaccess files, today we'll examine how to use mod_rewrite to create pretty URLs.

### Benefits of Formatted URLs

While some claim pretty URLs help in search engine rankings, the debate here is fierce. We can all agree that pretty URLs make things easier for our users and add a level of professionalism and polish to any web application.

I could go over all the theoretical reasons for this, but I like real-world examples better. Like it or hate it, we all must admit that Twitter is a wildly popular web application, and part of the reason for that is most certainly how it formats URLs. I can tell anyone in the know that my Twitter username is noahhendrix, and they know my profile can easily be found at twitter.com/noahhendrix. This seemingly simple concept has vast effects on the popularity of your application.

## Overview and Setup

If you plan to follow along, you need to install a PHP development environment on your computer, and you can go for either WAMP or XAMPP. Both of these packages come bundled with the Apache server, which provides the modules we are going to use.

Create the following folder and files in your web server root directory.

 1 demo/  2 ├── .htaccess  3 ├── home.php  4 ├── article.php  5 ├── profile.php 

The folder demo contains four files: home.php, article.php, profile.php, and .htaccess.

In the absence of an index.php file, we'll use .htaccess to set home.php as our index page. That way, it'll show at the URL localhost/demo/.

In the HTML markup for home.php, we'll provide a link to our article page.

 1   2   3   4  <-head->  5   6   7 

Semantic SEO Friendly with htaccess

 8  View Post  9   10  

We added a query string to the URL containing three variables that we want to pass to the article: year, month, and slug. Later, we'll use the .htaccess to configure our server to use a cleaner, well-structured URL whilst ensuring that the page still gets the query strings.

Inside article.php, we get these variables from the request URL and display them on the page:

 1   2   3   4   5   6   7 

Article Details

 8 

Post Year:

 9 

Post Month:

 10 

Post Slug:

 11   12  

## The .htaccess File

.htaccess is a server configuration file used to control websites. In using an Apache server, this file provides a way to reconfigure your server without having to manually edit the server configuration files.

Since we'll be working with the request URLs, we'll use the mod_rewrite Apache module to manipulate incoming URLs on the server-side.

We'll make all rewrites and reconfigurations inside the <IfModule> directive.

 1   2  # rules goes here  3  

The instructions inside this directive will run only if mod_rewrite is loaded by Apache. To make any modifications, we must first enable the RewriteEngine on our Apache server.

 1 # mod_rewrite  2 # directive  3   4  RewriteEngine On  5   6  Options -MultiViews  7   8  

In the nested directive just below, we set the environment up to follow symbolic links using the Options directive.

With the base configuration all set, let's take a look at some of the ways we can improve our URLs using Apache and PHP.

## Making URLs Cleaner With .htaccess

### Setting Alternative Index Files

Recall that our site doesn't have a default page, and this is because the index.php file is absent in the directory. In cases where you want to use an alternative PHP script file as your site's index, use DirectoryIndex

Below, we are manually making home.php our index page by pointing DirectoryIndex to that file.

 1 # mod_rewrite  2 # directive  3   4  # other code  5 6  DirectoryIndex home.php  7  

### Formatting URLs Using Apache

Unclean URLs expose the filenames of underlying server scripts and variables used in rendering the page. For example, a person can easily make out that the profile.php file is responsible for loading the page based on the URL example.com/profile.php?id=1.

By using clean URLs, you can help secure the site by hiding the structure of your application's back end. For example, based on the URL example.com/user/1, it's impossible to tell what server-side scripting file is responsible for rendering the page.

Let's match an alternative URL that we want the user to see:

 1 # Rewrite for projects.php  2 RewriteRule ^user profile.php [NC,L] 

The string of text you have after the caret symbol is the alternative directory listing that you want in place of the actual URL. Now when we navigate to /user on our site, it'll actually take us to profile.php

#### Passing Variables

Some of our URLs may contain dynamic variables. Take the following, for example: https://localhost/demo/article?year=2022&month=3&slug=using-htaccess-to-prettify-url

Going back to article.php, recall that we got the variables from the query string in the URL because the page requires these variables to render:

 1 

Post Year:

 2 

Post Month:

 3 

Post Slug:



To display the article page, we must add the query string to the URL: article?year=2022&month=3&slug=using-htaccess-to-prettify-url

This URL is neither SEO- nor user-friendly. A significantly better URL would be: article/2022/3/using-htaccess-to-prettify-url

However, changing the URL to this will give an Undefined Index error because the query string is no longer being sent to the page via the URL, and this is where .htaccess comes into play.

To solve this problem, we need to write a regular expression (regex) to match our intended semantic URL, and then pass query variables from each capture group to article.php:

 1 RewriteRule ^article/([0-9]+)/([0-9]+)/([0-9A-Za-z\-_]+) article.php?year=$1&month=$2&slug=$3 [NC, L]  We'll only match a URL that contains the path articles/ followed by a double-digit number for the article year and month, followed by a string for the post slug which could contain text, numbers, hyphens, and an underscore. When a request's URL matches the provided regex, we tell the server to redirect to article.php. Most importantly, we pass the query strings along to the page. For the query string, the three capture groups, denoted by $1, $2, and $3 respectively, are all transplanted into the variables year, month, and slug.

We end the rule with two flags. The first flag, NC (Non-case sensitive), tells Apache not to consider the case when matching the URL with our regex. The second flag, L, tells Apache to stop code execution thereafter.

Finally, replace +SymLinksIfOwnerMatch with -MultiViews.

 1   2  # Options +SymLinksIfOwnerMatch  3  Options -MultiViews  4   5  # other code  6  

Navigate to localhost/demo/article/2022/3/using-htaccess-to-prettify-url on your browser and you'll find that our page is still able to access the variables without using URL query strings.

Keep in mind that the order is important. So, for example, if you reorder the variables in the query string like in the following instance:

 1 article.php?slug=$1&month=$2&year=$3  You also need to reorganize the regular expression groups to match them. ### Using PHP This next method is great for those who don't want to distribute too much logic to Apache and feel more comfortable in PHP (or similar scripting languages). The concept here is to capture any URL the server receives and push it to a PHP controller page. This comes with the added benefit of control, but greater complexity at the same time. Your .htaccess file might look something like this:  1   2  RewriteEngine On  3 4   5  Options -MultiViews  6   7   8  DirectoryIndex home.php  9 10  RewriteCond %{REQUEST_FILENAME} !-d  11  RewriteCond %{REQUEST_FILENAME}\.php -f  12 13  RewriteRule ^.*$ ./home.php  14  

Instead of creating a capture group, we just tell Apache to grab every URL and redirect it to home.php. What this means is we can do all of our URL handling in PHP without relying too much on stringent URL paths in .htaccess. Here is what we might do at the top of our home.php file to parse out the URL:

 1 

The first line is not necessary unless your application doesn't live at the root directory, like my demos. I am removing the non-sense part of the URL that I don't want PHP to worry about. $_SERVER['REQUEST_URI'] is a global server variable that PHP provides and stores the request URL, it generally looks like this:  1 demo/article/query1/query2/...  As you can see, it is basically everything after the domain name. Next, we split up the remaining part of the virtual path and split it by the / character. This allows us to grab individual variables. One thing you might do is take the first element of the $params array and include a file by that same name. Then, within the file, you can use the second, third, and subsequent elements in the array (i.e. the queries) to execute some code (such as fetching from the database). This might look something like this:

 1  
The first part of this code is unbelievably important! You absolutely must restrict what pages a user can get so they don't have the opportunity to print out any page they wish by guessing at file names, like a database configuration file.

Now that we have the soapbox out of the way, let's move on. Next, we check if the requested file is in the $safe_pages array, and if it is, we include—otherwise, we will include a 404 not found page. On the included page, you will see that you have access to the $params array, and you can grab whatever data from it that is necessary for your application.

This is great for those who want a little more control and flexibility. It obviously requires quite a bit of extra code, so it's probably better for new projects that won't require a lot of code to be updated to fit the new URL formats.

### A Simple URL Shortener

This last part of the tutorial is going to let us put the code we went over above into practice, and it's more or less a "real-life" example. We are going to create a service called shrtr (I made up this name, so any other products with this name are not associated with the code I am posting below). I know this is not an original concept, and it's only meant for a demonstration of mod_rewrite. First, let's take a look at the database:

As you can see, this is very straightforward. We have only four columns:

• id: unique identifier used to reference specific rows
• short: unique string of characters appended to the end of our URL to determine where to redirect
• url: the URL that the short URL redirects to
• created_at: a simple timestamp so we know when this URL was created

### The Basics

Next, let's go over the six files we need to create for this application:

• .htaccess: redirects all short URLs to serve.php
• create.php: validates URL, creates shortcode, saves to the database
• css/style.css: holds some basic styling information
• db_config.php: store variables for database connections
• index.php: the face of our application with a form for entering URLs
• serve.php: looks up a short URL and redirects to an actual URL

That is all we need for our basic example. I will not cover index.php or css/style.css in very great detail because they are static files with no PHP.

 1   2   3   4   5   6   7  Make URL shorter  8   9   10 
 11 

shrtr.me

 12   13 
 14 
 15   16  Type your URL here  17   18   19   20   21 
 22   23 
 24   25  

The only interesting to note here is that we submit the form with a field called URL to create.php.

 1 # css/style.css  2 ----  3 /* reset */  4 * {  5  font-family: Helvetica, sans-serif;  6  margin: 0;  7  padding: 0;  8 }  9   10 /* site */  11 html, body { background-color: #008AB8; }  12 a { color: darkblue; text-decoration: none;}  13   14  #pagewrap {  15  margin: 0 auto;  16  width: 405px;  17  }  18   19  h1 {  20  color: white;  21  margin: 0;  22  text-align: center;  23  font-size: 100px;  24  }  25  h1 .r { color: darkblue; }  26   27  .body {  28  border-radius: 10px;  29  border-radius: 10px;  30  background-color: white;  31  text-align: center;  32  padding: 50px;  33  height: 80px;  34  position: relative;  35  }  36   37  .body .instructions {  38  display: block;  39  margin-bottom: 10px;  40  }  41  .body .back {  42  right: 15px;  43  top: 10px;  44  position: absolute;  45  }  46   47  .body input[type=text] {  48  display: block;  49  font-size: 20px;  50  margin-bottom: 5px;  51  text-align: center;  52  padding: 5px;  53  height: 20px;  54  width: 300px;  55  } 

That is all very generic, but it makes our application a little more presentable.

The last basic file we need to look at is our db_config.php. I created this to abstract some of the database connection information.

 1 # db_config.php  2 ----  3  

You need to replace the values with what works in your database, and host is probably localhost, but you need to double-check with your hosting provider to make sure. Here is the SQL dump of the table, url_redirects, which holds all the information we showed above:

 1 --  2 -- Table structure for table url_redirects  3 --  4   5 CREATE TABLE IF NOT EXISTS url_redirects (  6  id int(11) NOT NULL auto_increment,  7  short varchar(10) NOT NULL,  8  url varchar(255) NOT NULL,  9  created_at timestamp NOT NULL default CURRENT_TIMESTAMP,  10  PRIMARY KEY (id),  11  KEY short (short)  12 ) ENGINE=MyISAM DEFAULT CHARSET=utf8; 

### Creating the Short URL

Next, let's look at the code necessary to create our short URL.

 1 # create.php  2 ----  3 shrtr.me/".$short;  18  } else {  19 $html = "Error: cannot find database";  20  }  21   22  mysql_close($db);  23  }  24 ?>  25 26   27   28   29   30   31   32  Make URL shorter  33   34   35   36  shrtr.me  37   38   39   40   41  X  42   43   44   Now we are getting a bit more complex! First, we need to include the database connection variables we created earlier, and then we store the URL parameter sent to us by the create form in a variable called $url. Next, we do some regular expressions magic to check if they actually sent a URL, and if not, we store an error.

If the user entered a valid URL, we create a connection to the database using the connection variables we include at the top of the page. Next, we generate a random five-character string to save to the database, using the substr function. The string we split up is the MD5 hash of the current time() and $url concatenated together. Then we insert that value into the url_redirects table along with the actual URL, and store a string to present to the user. If it fails to insert the data, we store an error. If you move down into the HTML part of the page, all we do is print out the value of $html, be it error or success. This obviously isn't the most elegant solution, but it works!

### Serving the Short URL

So we have the URL in the database. Now, let's work on serve.php so we can actually translate the short code into a redirect.

 1   19 20   21   22   23   24   25   26  Make URL shorter  27   28   29 
 30 

shrtr.me

 31   32 
 33   34 

 35  X  36 
 37   38 
 39   40  

This one is very similar to create.php: we include the database information and store the short code sent to us in a variable called $short. Next, we query the database for the URL of that short code. If we get a result, we redirect to the URL; if not, we print out an error like before. As far as PHP goes, that is all we need to do. However, at the moment, to share a short URL, users must enter this: http://shrtr.me/server.php?short=SHORT_CODE. Not very pretty, is it? Let's see if we can't incorporate some mod_rewrite code to make this nicer. ### Prettify With .htaccess Of the two methods I wrote about at the beginning of the tutorial, we will use the Apache one because this application is already created without considering any URL parsing. The code will look something like this:  1   2  RewriteEngine On  3 4   5  Options +FollowSymLinks  6   7 8  RewriteCond %{SCRIPT_FILENAME} !-d  9  RewriteCond %{SCRIPT_FILENAME} !-f  10 11  RewriteRule ^(\w+)$ ./serve.php?short=\$1  12  

Skipping to the RewriteRule, we are directing any traffic that doesn't already have a real file or directory to serve.php and putting the extension in the GET variable short. Not too bad—now go try it out for yourself!

## Conclusion

Today, we learned a few different ways to utilize mod_rewrite` in our application to make our URLs pretty. Thanks for reading!

This post has been updated with contributions from Kingsley Ubah. Kingsley is passionate about creating content that educates and inspires readers. Hobbies include reading, football, and cycling.