The author selected the Diversity in Technology Fund to receive a grant as part of the Write for DOnations program.
Apache’s mod_rewrite module allows you to rewrite URLs more cleanly, translating human-readable paths into code-friendly query strings. It also allows you to rewrite URLs based on conditions.
An .htaccess file allows you to create and apply rewrite rules without accessing server configuration files. By placing the .htaccess file at the root of your website, you can manage rewrites by site or by directory.
In this tutorial, you’ll enable mod_rewrite and use .htaccess files to create a basic URL redirect and then explore a couple of advanced use cases.
Prerequisites
To follow this tutorial, you will need:
An Ubuntu 20.04 server
-
configured following the Ubuntu 20.04 server initial setup guide, including a non-root sudo user and a firewall.
-
Apache installed following Step 1 of How to install Linux, Apache, MySQL, PHP (LAMP) stack on Ubuntu 20.04.
Step 1 — Enabling
mod_rewrite
For Apache to understand the rewrite rules, we must first enable mod_rewrite. It is already installed, but it is disabled in a default Apache installation. Use
the a2enmod command to enable the module:
- sudo a2enmod rewrite
This will activate the module or alert you that the module is already enabled. To implement these changes, restart Apache.
- sudo systemctl restart apache2
mod_rewrite is now fully enabled. In the next step, we will configure an .htaccess file that we will use to define rewrite rules for redirects.
Step 2 — Configure .
htaccess
An .htaccess file allows us to modify our rewrite rules without accessing the server configuration files. For this reason, .htaccess is critical to the security of your web application. The period preceding the file name ensures that the file is hidden.
Before you start using .htaccess files, you’ll need to configure and secure a few more settings.
By default, Apache prohibits the use of an .htaccess file to apply rewrite rules, so you must first allow changes to the file. Open the default Apache configuration file using nano or your favorite text editor.
- sudo nano /etc/apache2/sites-available/000-default.conf
Inside that file, you’ll find a <VirtualHost *:80> block that starts on the first line. Within that block, add the next new block to make your configuration file look like the next one. Make sure all blocks are properly indented.
<VirtualHost *:80> <Directory /var/www/html>Options Indexes FollowSymLinksAllowOverride AllRequire all grant</Directory> . . . </VirtualHost>
Save and close the file. To implement these changes, restart Apache.
- sudo systemctl restart apache2
Now, create an
.htaccess file in the web root.
- sudo nano /var/www/html/.htaccess
Add this line at the top of the new file to activate the rewrite engine.
RewriteEngine in
Save the file and exit
.
We now have a working .htaccess file that we can use to govern the routing rules of our web application. In the next step, we’ll create sample website files that we’ll use to demonstrate the rewrite rules.
Step 3 — Setting up URL rewrites
Here, we’ll set up a basic URL rewrite that turns pretty URLs into actual paths to pages. Specifically, we will allow users to access http://your_server_ip/about, but display a page called about.html.
Start by creating a file called about.html in the web root.
- sudo nano /var/www/html/about.html
Copy the following HTML code into the file, save it, and close it.
<html> <head> <title>About Us</title> </head> <body> <h1>About Us</h1> </body> </html>
You can access this page at http://your_server_ip/about.html, but notice that if you try to access http://your_server_ip/about, you will see a 404 Not Found error. To access the page using /about instead, we’ll create a rewrite rule.
All RewriteRules follow this format:
RewriteRule pattern substitution [flags]
- RewriteRule specifies the policy
- pattern is a regular expression that matches the desired string
- substitution is the path to the actual URL, i.e. the path of the Apache file servers.
- Indicators are optional parameters that can modify the operation of the rule.
.
of the URL, which is what the viewer types in the browser.
Let’s create our URL rewriting rule. Open the .htaccess file.
- sudo nano /var/www/html/.htaccess
After the first line, add the highlighted RewriteRule and save the file.
RewriteEngine in RewriteRule ^about$ about.html [NC]
In this case, ^about$ is the pattern, about.html is the substitution, and [NC] is a flag. Our example uses some characters with a special meaning:
^ indicates the start of the URL
- after your_server_ip/
- $ indicates the end of the
- matches the string “about”. about.html
- actual file that the user accesses
- [NC] is an indicator that makes the case for the rule insensitive.
.
URL. about
is the
.
You can now access http://your_server_ip/about in your browser. In fact, with the rule shown above, the following URLs will point
to about.html: http://your_server_ip/about, due to the rule definition. http://your_server_ip/About, because the
- rule is not case sensitive. http://
- your_server_ip/about.html, because the original proper filename will always work.
However, the following will not
work:
http://your_server_ip/about/, because the rule
- explicitly states that there can be nothing after about, since the $ character appears after about. http://your_server_ip/
- contact, because it will not match the string about the
rule.
You now have a working .htaccess file with a basic rule that you can modify and extend according to your needs. In the following sections, we will show two additional examples of commonly used policies.
Example 1: Simplifying query strings with RewriteRule Web
applications typically use query strings, which are appended to a URL by a question mark (?) after the address. Separate parameters are delimited using an ampersand (&). Query strings can be used to pass additional data between individual application pages.
For example, a search results page written in PHP might use a URL as http://example.com/results.php?item=shirt&season=summer. This example passes two additional parameters to the imaginary result.php application script: item, with the value shirt, and season with the value summer. The application can use the query string information to create the correct page for the visitor.
Apache rewrite rules are often employed to simplify links as long and unpleasant as the ones above into friendly URLs that are easier to write and interpret visually. In this example, we would like to simplify the link above so that it becomes http://example.com/shirt/summer. The values of the shirt and summer parameters are still at the address, but without the query string and script name.
Here is a rule to implement this:
RewriteRule ^shirt/summer$ results.php?item=shirt&season=summer [QSA]
The shirt/summer explicitly matches the requested address and Apache is told to serve results.php?item=shirt&season=summer instead
.
[QSA] flags are commonly used in rewrite rules. They tell Apache to add any additional query strings to the served URL, so if the visitor types http://example.com/shirt/summer?page=2 the server will respond with results.php?item=shirt&season=summer&page=2. Without it, the additional query string would be discarded.While this method achieves the desired effect, both the article name and the season are coded in the ruler. This means that the rule will not work for any other items, such as pants, or seasons, such as winter.
To make the rule more generic, we can use regular expressions to match parts of the original address and use those parts in a substitution pattern. The modified rule will look like this:
RewriteRule ^([A-Za-z0-9]+)/(summer|winter|autumn|spring) results.php?item=$1&season=$2 [QSA]
The first group of regular expressions in parentheses matches a string containing alphanumeric characters and numbers such as shirt or pants and saves the matching fragment as the $1 variable. The second group of regular expression in parentheses coincides exactly with summer, winter, autumn or spring, and similarly saves the matching fragment as $2.
The matching snippets are used in the resulting URL in the item and season variables instead of the hardcoded shirt and summer values we used before.
This will convert, for example, http://example.com/pants/summer into http://example.com/results.php?item=pants&season=summer. This example is also future-proof, allowing multiple items and seasons to be rewritten correctly using a single rule.
Example 2 — Add conditions with logic using RewriteConds
Rewrite rules are not always necessarily evaluated one by one without any limitation. The RewriteCond directive allows us to add conditions to our rewrite rules to control when the rules are processed. All RewriteConds conform to the following format:
RewriteCond Condition TestString [Flags] RewriteCond specifies the
- RewriteCond directive
- TestString is the string to test with
- Condition is the pattern or condition that must match
- Indicators are optional parameters that can modify the condition and evaluation rules.
.
.
.
If a RewriteCond evaluates to true, it will be considered RewriteRule immediately following. If you don’t, the rule will be discarded. Multiple RewriteConds can be used one after the other, and with the default behavior, they must all be evaluated to true for the following rule to be taken into account.
As an example, suppose you want to redirect all requests to nonexistent files or directories on your site to the home page instead of displaying the standard 404 Not Found error page. This can be achieved with the following condition rules:
RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule. / [R=301]
With the above:
- %{REQUEST_FILENAME} is the string to check. In this case, it is the requested file name, which is a system variable available for each request.
- -f is a built-in condition that checks whether the requested name exists on the disk and is a file. The! is a negation operator. Combined, !-f evaluates to true only if a specified name does not exist or is not a file.
- Similarly, !-d evaluates to true only if a specified name does not exist or is not a directory.
RewriteRule on the final line will take effect only for requests to non-existent files or directories. The RewriteRule itself is very simple. The point . In the pattern matches anything and substitution directs each request to the root/website.
In addition, the
[R=301] flag tells Apache to return a 301 Permanently Moved redirect HTTP response code to the browser, which makes a browser aware that the redirect occurred and explicitly get the root of the website instead of the requested URL, with the change reflected in the browser’s address bar.Without this flag, Apache would return the root content of the website, but the browser would still think that the URL of the requested page exists and display the originally requested address in the address bar.
mod_rewrite allows you to create human-readable URLs. In this walkthrough, you used the RewriteRule directive to redirect URLs, including URLs with query strings. You also wrote conditional redirect URLs using the RewriteCond directive.
If you want to learn more about mod_rewrite, take a look at the Apache mod_rewrite Introduction and official Apache documentation for mod_rewrite.