An Interest In:
Web News this Week
- March 31, 2024
- March 30, 2024
- March 29, 2024
- March 28, 2024
- March 27, 2024
- March 26, 2024
- March 25, 2024
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 adds 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 in 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, 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.
demo/
├── .htaccess
├── home.php
├── article.php
├── profile.php
The folder demo contains three 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.
<!DOCTYPE html>
<html lang="en">
<head>
<-head->
</head>
<body>
<h1 style="text-align: center"> Semantic SEO Friendly with htaccess</h1>
<a href="article?year=2022&month=3&slug=using-htaccess-to-prettify-url">View Post</a>
</body>
</html>
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:
<!DOCTYPE html>
<html lang="en">
<head>
</head>
<body>
<h1 style="text-align: center"> Article Details </h1>
<p>Post Year: <?php echo $_GET['year']?></p>
<p>Post Month: <?php echo $_GET['month']?></p>
<p>Post Slug: <?php echo $_GET['slug']?></p>
</body>
</html>
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 of the <IfModule>
directive.
<IfModule mod_rewrite.c>
# rules goes here
</IfModule>
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.
# mod_rewrite
# directive
<IfModule mod_rewrite.c>
RewriteEngine On
<IfModule mod_negotiation.c>
Options -MultiViews
</IfModule>
</IfModule>
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 file index.php 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.
# mod_rewrite
# directive
<IfModule mod_rewrite.c>
# other code
DirectoryIndex home.php
</IfModule>
Formatting URLs using Apache
Unclean URLs expose 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 backend. 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:
# Rewrite for projects.php
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:
<p>Post Year: <?php echo $_GET('year')?></p>
<p>Post Month: <?php echo $_GET('month')?></p>
<p>Post Slug: <?php echo $_GET('slug')?></p>
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, then pass query variables from each capture group to article.php:
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 number, hyphen 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 ended 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
.
<IfModule mod_negotiation.c>
# Options +SymLinksIfOwnerMatch
Options -MultiViews
# other code
</IfModule>
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:
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:
<IfModule mod_rewrite.c>
RewriteEngine On
<IfModule mod_negotiation.c>
Options -MultiViews
</IfModule>
DirectoryIndex home.php
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteRule ^.*$ ./home.php
</IfModule>
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:
<?php
#replace demo/ in the request URL with an empty string
$request = str_replace("/demo/", "", $_SERVER['REQUEST_URI']);
#split the path by '/'
$params = split("/", $request);
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:
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 and within the file, you can use the second, third and subsequent elements in the array (ie. the queries) to execute some code (such as fetching from the database). This might look something like this:
<?php
#keeps users from requesting any file they want
$safe_pages = array("users", "profile", "article");
if(in_array($params[0], $safe_pages)) {
include($params[0].".php");
} else {
include("404.php");
}
?>
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 will include a 404 not found page. In 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 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 Shortner
This last part of the tutorial is going to let us put some use to the code we went over above, and is 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 is only meant for demonstration of mod_rewrite. First let's take a look at the database:
As you can see this is very straightforward, we have only 4 columns:
id
: unique identifier used to reference specific rowsshort
: unique string of characters appended to the end of our URL to determine where to redirecturl
: the URL that the short URL redirects tocreated_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 form for entering URL
serve.php: looks up short URL and redirects to 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 have no PHP, and are static files.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Make URL shorter</title>
</head>
<body>
<div id="pagewrap">
<h1>shrt<span class="r">r</span>.me</h1>
<div class="body">
<form action="./create.php" method="post">
<span class="instructions">Type your URL here</span>
<input name="url" type="text" />
<input type="submit" value="shrtr" />
</form>
</div>
</div>
</body>
</html>
The only real interesting to note here is that we submit the form with a field called URL to create.php.
# css/style.css
----
/* reset */
* {
font-family: Helvetica, sans-serif;
margin: 0;
padding: 0;
}
/* site */
html, body { background-color: #008AB8; }
a { color: darkblue; text-decoration: none;}
#pagewrap {
margin: 0 auto;
width: 405px;
}
h1 {
color: white;
margin: 0;
text-align: center;
font-size: 100px;
}
h1 .r { color: darkblue; }
.body {
border-radius: 10px;
border-radius: 10px;
background-color: white;
text-align: center;
padding: 50px;
height: 80px;
position: relative;
}
.body .instructions {
display: block;
margin-bottom: 10px;
}
.body .back {
right: 15px;
top: 10px;
position: absolute;
}
.body input[type=text] {
display: block;
font-size: 20px;
margin-bottom: 5px;
text-align: center;
padding: 5px;
height: 20px;
width: 300px;
}
That is all very generic, but 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.
# db_config.php
----
<?php
$database = "DATABASE_NAME";
$username = "USERNAME";
$password = "PASSWORD";
$host = "localhost";
?>
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
that holds all the information we showed above:
--
-- Table structure for table `url_redirects`
--
CREATE TABLE IF NOT EXISTS `url_redirects` (
`id` int(11) NOT NULL auto_increment,
`short` varchar(10) NOT NULL,
`url` varchar(255) NOT NULL,
`created_at` timestamp NOT NULL default CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `short` (`short`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
Creating the Short URL
Next lets look at the code necessary to create our short URL.
# create.php
----
<?php
require("./db_config.php");
$url = $_REQUEST['url'];
if(!preg_match("/^[a-zA-Z]+[:\/\/]+[A-Za-z0-9\-_]+\\.+[A-Za-z0-9\.\/%&=\?\-_]+$/i", $url)) {
$html = "Error: invalid URL";
} else {
$db = mysql_connect($host, $username, $password);
$short = substr(md5(time().$url), 0, 5);
if(mysql_query("INSERT INTO `".$database."`.`url_redirects` (`short`, `url`) VALUES ('".$short."', '".$url."');", $db)) {
$html = "Your short URL is<br />shrtr.me/".$short;
} else {
$html = "Error: cannot find database";
}
mysql_close($db);
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Make URL shorter</title>
</head>
<body>
<div id="pagewrap">
<h1>shrt<span class="r">r</span>.me</h1>
<div class="body">
<?= $html ?>
<br /><br />
<span class="back"><a href="./">X</a></span>
</div>
</body>
</html>
Now we are getting a bit more complex! First we need to include the database connection variables we created earlier, 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, 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 page. Next we generate a random 5-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 let's work on serve.php so we can actually translate the short code into a redirect.
<?php
require("./db_config.php");
$short = $_REQUEST['short'];
$db = mysql_connect($host, $username, $password);
$query = mysql_query("SELECT * FROM `".$database."`.`url_redirects` WHERE `short`='".mysql_escape_string($short)."' LIMIT 1", $db);
$row = mysql_fetch_row($query);
if(!empty($row)) {
Header("HTTP/1.1 301 Moved Permanently");
header("Location: ".$row[2]."");
} else {
$html = "Error: cannot find short URL";
}
mysql_close($db);
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Make URL shorter</title>
</head>
<body>
<div id="pagewrap">
<h1>shrt<span class="r">r</span>.me</h1>
<div class="body">
<?= $html ?>
<br /><br />
<span class="back"><a href="./">X</a></span>
</div>
</div>
</body>
</html>
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, but 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.
Pretty-ify 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:
<IfModule mod_rewrite.c>
RewriteEngine On
<IfModule mod_negotiation.c>
Options +FollowSymLinks
</IfModule>
RewriteCond %{SCRIPT_FILENAME} !-d
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteRule ^(\w+)$ ./serve.php?short=$1
</IfModule>
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 to bad no 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.
Original Link: https://code.tutsplus.com/tutorials/using-htaccess-files-for-pretty-urls--net-6049
TutsPlus - Code
Tuts+ is a site aimed at web developers and designers offering tutorials and articles on technologies, skills and techniques to improve how you design and build websites.More About this Source Visit TutsPlus - Code