---
date: 2023-12-20
title: Do’s and don’ts of redirects with .htaccess
description: Things to know when implementing redirects with Apache
tags: http, redirect, apache
---

# Do’s and don’ts of redirects with .htaccess

20/12/2023 in #http #apache #redirect

I have spent many hours configuring thousands of redirects when migrating websites, following a redesign or a change of stack. In this article, I am sharing some of the do's and don'ts learned along the way and that I would recommend for anyone working on this kind of tasks to follow.

## Basics of URL structure

To understand how to set up URL redirects, it's best to understand the structure of a URL and the terms used to describe each part. Here's a quick diagram that sums it up:

<div class="group-leading-none">

```
https://www.fqdn.com:443/blogs/?sort=latests#fragment
└┬──┘   └─┬─────────┘└┬┘└─┬───┘└─┬─────────┘└───┬───┘
 └ scheme │      port ┘   │      │              └ fragment
          │               │      └ query string
          └ domain        └ path
```

</div>

For more details on URL structure, make sure to check [MDN's documentation](https://developer.mozilla.org/en-US/docs/Learn/Common_questions/Web_mechanics/What_is_a_URL) and for front-end devs used to work with the `window.location` object, the following diagram can be used to compare with the previous one:

<div class="group-leading-none">

```
  ┌ protocol          ┌ port
┌─┴──┐               ┌┴┐
https://www.fqdn.com:443/blogs/?sort=latests#fragment
│       └──┬───────┘   │└─┬──┘ └─┬─────────┘└─┬─────┘
│       │  └ hostname  │  │      └ search     └ hash
│       └──┬───────────┘  └ pathname
│          └ host      │
└──────────┬───────────┘
           └ origin
```

</div>

## Basics of redirects with Apache

Apache has 2 main directives which can be used to configure redirects:

- `Redirect`
- `RedirectMatch`

They can be used to define redirects from an old path to a new URL simply:

```apache
Redirect /old-path https://fqdn.com/new-path
RedirectMatch /old-path https://fqdn.com/new-path
```

Both directives will default to a `302` temporary redirect, if you need a permanent one, make sure to specify the `301` status:

```apache
Redirect 301 /old-path https://fqdn.com/new-path
RedirectMatch 301 /old-path https://fqdn.com/new-path
```

The `RedirectPermanent` directive can also be used instead of `Redirect 301`:

```apache
RedirectPermanent /old-path https://fqdn.com/new-path
```

Note that the new URL can have a different domain than the original one, i.e. we can redirect from `domain-a.com` to `domain-b.com`.

## Do’s and don’ts

### Prefer specific redirects to overly generic rules

It's easier to specifically test redirected URLs when they're complete, rather than defining a matching pattern. You'll also have better control over the impact of your redirect rules on your site, and avoid false positives.

```apache
# Do
RedirectPermanent /blog/page/1 https://fqdn.com/
RedirectPermanent /blog/page/2 https://fqdn.com/
RedirectPermanent /blog/page/3 https://fqdn.com/
RedirectPermanent /blog/page/4 https://fqdn.com/

# Don't
RedirectMatch /blog/page/* https://fqdn.com/
```

### `Redirect` directives may retain parts of the old path

When a URL matches a `Redirect` directive (`Redirect`, `RedirectMatch`, `RedirectPermanent`), the part of the URL that doesn't match will be added to the redirect URL.

Thus, with the following configurations:

```apache
RedirectPermanent /foo https://fqdn.com/redirected
# or with RedirectMatch
RedirectMatch 301 ^/foo https://fqdn.com/redirected
```

The following redirects will happen:

```
https://fqdn.com/foo      → https://fqdn.com/redirected
https://fqdn.com/foo/     → https://fqdn.com/redirected/
https://fqdn.com/foo/bar  → https://fqdn.com/redirected/bar
https://fqdn.com/foo/bar/ → https://fqdn.com/redirected/bar/
```

To ensure that an exact URL redirects to another exact URL without keeping parts of the original URL, use the `RedirectMatch` directive with a `.*` pattern:

```apache
RedirectMatch 301 ^/foo.* https://fqdn.com/redirected
```

The following redirects will happen:

```
https://fqdn.com/fo       → https://fqdn.com/fo
https://fqdn.com/foo      → https://fqdn.com/redirected
https://fqdn.com/foo/     → https://fqdn.com/redirected
https://fqdn.com/foo/bar  → https://fqdn.com/redirected
https://fqdn.com/foo/bar/ → https://fqdn.com/redirected
```

### The order of redirect rules is important

Redirect rules in the `.htaccess` file must be added from the most specific one to the most generic one. The first rule matching the request URL is followed and other rules are ignored.

So, with the following configuration:

```apache
RedirectPermanent /foo https://fqdn.com/bar
RedirectPermanent /foo/bar https://fqdn.com/foo/baz
```

The URL `https://fqdn.com/foo/bar` will be redirected by the first redirect rule, while the second rule will never be evaluated.

To fix this problem, a simple trick is to sort your redirect rules in reverse alphabetical order:

```apache
RedirectPermanent /foo/bar https://fqdn.com/foo/baz
RedirectPermanent /foo https://fqdn.com/bar
```

### `Redirect` directives ignore URL parameters

If the URL that needs to be redirected contains URL parameters (e.g. `?search=keyword`), `Redirect` directives will have no effect on the request.

For example, with the following configuration:

```apache
RedirectPermanent /foo/?search=news https://fqdn.com/fr/
```

A request with the `https://fqdn.com/foo?search=news` URL will not be handled.

Redirects including URL parameters must be set up with `RewriteCond` and `RewriteRule`:

```apache
RewriteCond %{QUERY_STRING} ^search=news$
RewriteRule ^foo$ https://doo.com/? [L,R=301]
```

Make sure to enable the `RewriteEngine` mode before using `RewriteCond` directives:

```apache
RewriteEngine On
RewriteBase /
```

## Validating your `.htaccess` file

Before deploying your redirect rules in production, it is important to test and validate them. First because any syntax error in a ´.htaccess´ file will result in an error 500 when accessing the website. Second, to ensure that each rule send your users to the correct location.

### online htaccess tester

For simple rules, you can use the very good and useful [htaccess tester](https://htaccess.madewithlove.com/) by [madewithlove](https://madewithlove.com/).

### studiometa/test-redirection

If you have hundreds of rules or more, I would recommend to try [the tool](https://github.com/studiometa/cli-test-redirection) we createt at [Studio Meta](https://www.studiometa.fr/). It can be used with Docker, and will test Apache redirects defined in a CSV file, with host mocking and `https` support, useful to test before going live.

```sh
# Create your .htaccess file with redirections to test
$ cat .htaccess
RedirectPermanent /foo/bar https://www.fqdn.com/foo/baz
RedirectMatch 301 ^/foo$ https://www.fqdn.com/bar

# Create a CSV file with 2 columns "from" and "to"
$ cat redirects.csv
from,to
https://fqdn.com/foo/bar,https://www.fqdn.com/foo/baz
https://fqdn.com/foo,https://www.fqdn.com/bar

# Run the Docker image by linking the current directy to /app
$ docker run -it --rm -v $PWD:/app -e "DOMAINS=fqdn.com www.fqdn.com" \
  studiometa/test-redirection redirects.csv --verbose

[1/2] ✅ https://fqdn.com/foo
  → https://www.fqdn.com/bar
[2/2] ✅ https://fqdn.com/foo/bar
  → https://www.fqdn.com/foo/baz

🟢 All 2 redirection tests passed.

```

Or you can use the CLI directly with [`npx`](https://docs.npmjs.com/cli/v11/commands/npx) to test your redirects on your live site:

```sh
$ cat redirects.csv
from,to
https://fqdn.com/foo/bar,https://www.fqdn.com/foo/baz
https://fqdn.com/foo,https://www.fqdn.com/bar

$ npx @studiometa/cli-test-redirection redirects.csv --verbose

[1/2] ✅ https://fqdn.com/foo
  → https://www.fqdn.com/bar
[2/2] 🚫 https://fqdn.com/foo/bar
  → - https://www.fqdn.com/foo/baz
  → + https://fqdn.com/foo/bar

🟢 1 out of 2 tests passed.
🔴 1 out of 2 tests failed.
```

Make sure to check out the [GitHub repository](https://github.com/studiometa/cli-test-redirection) to learn more!
