Using Apache RewriteMap to handle redirects for WordPress multisite

I’m assuming you’re here for answers, so I’m going to put the solution first and the backstory details later.

Quick solution: How to use Apache RewriteMap

Step 1: Create the redirect mapping file.

One entry per line. Source URI, single space, then the destination URI. Remember, RewriteMap only handles simple redirects (no regex, wildcard, or conditional redirects are allowed).

Choose the scenario that fits your needs:

Step 2: Create the indexed flat-file database to improve lookup speed.

sudo httxt2dbm -f db -i ~/redirects.txt -o /var/www/acmeinc/redirects.db

If your server complains that httxt2dbm isn’t installed, try installing the apache2-utils package.

Step 3: Tell Apache to use the redirect mapping file.

sudo vi /etc/apache2/sites-available/acmeinc.conf

<VirtualHost *:443>

  ServerName www.acmeinc.com

  # Declare the rewritemap
  RewriteMap acmeredirects "dbm:/var/www/acmeinc/redirects.db"

  # Prevent serving the redirects.db file
  <Files "redirects.db">
    Require all denied
  </Files>

  <Directory /var/www/acmeinc>

    Options FollowSymLinks MultiViews
    AllowOverride All
    Require all granted

    RewriteEngine On

    # Make these the first rewrite/redirect rules, before the WP stuff.

    # Uncomment the lines for the scenario that meets your needs:

    # (SCENARIO A) For a single domain, just process URI
    # RewriteCond ${acmeredirects:$1} !=""

    # (SCENARIO B) For multisite, handle the hostname and URI
    # RewriteCond ${acmeredirects:%{HTTP_HOST}%{REQUEST_URI}} !=""
    # RewriteRule .* ${acmeredirects:%{HTTP_HOST}%{REQUEST_URI}} [R=301,END]

    # Regular WP redirect lines goes here
    RewriteCond %{REQUEST_FILENAME} -f [OR]
    RewriteCond %{REQUEST_FILENAME} -d
    RewriteRule ^ - [L]
    RewriteRule ^(wp-(content|admin|includes).*) $1 [L]
    RewriteRule ^(.*\.php)$ $1 [L]
    RewriteRule . index.php [L]

  </Directory>

</VirtualHost>

Step 4: Restart Apache!

sudo systemctl restart apache2

Test out the redirects to make sure they work. At this point, you’re done!

Results

This section added: 2020-06-11

Now that our system has been running for a few days, I wanted to compare site performance to see if there were any noticable improvements.

It turns out that removing the WordPress redirection plugin and allowing Apache to handle the redirects has resulted in significant performance gains for our website. Overall page load time has decreased by a full second. TTFB has been cut in half, along with first contentful paint. Onload and fully loaded page times are seeing huge improvements as well.

A graph showing that the TTFB and page load times decreased significantly as a result of removing the WordPress redirection plugin.

Index Before After Reduction
TTFB 439 219 -50%
FCP 1300 600 -54%
Onload 2500 1400 -44%
Fully loaded 2700 1700 -37%

Backstory

WordPress has some great redirect plugins, but in order to improve performance and ensure stability, I avoid third-party plugins as much as possible.

I have used RewriteMap before for site redirect handlers, but learning how to use it for a multi-domain WordPress multisite install took some intense sleuthing.

What’s Apache RewriteMap?

It’s a really fast way to handle thousands of redirects at the server level, before the request even gets to PHP. A simple, plain text file stores the mapping of all(1) redirects you want to handle.

# An example Apache RewriteMap file with two redirects
/from/this/url /to/this/url
/old/url /new/url

Apache even comes with a utility to create an indexed database of that list, so that URL lookups are ordered and nearly instantaneous.

(1) Regex redirects need to be put in your VirtualHost file.

Why use RewriteMap?

By taking PHP out of the equation and allowing Apache to handle your redirects, just look at all the processing steps that are saved on each page request:

  1. Apache receives the incoming HTTP request.
  2. An SSL handshake happens, and a secure connection is opened.
  3. ⇒ Apache parses the request and applies any redirect rules. ⇐
  4. Apache sends the request to the PHP engine.
    1. PHP loads the WordPress core and theme files (hopefully from the in-memory opcache) and starts executing WordPress:
      1. wp-config.php is parsed and executed.
      2. Default constants are set up.
      3. Connection to the DB is established.
      4. wp-includes/cache.php runs.
      5. sunrise.php runs (if applicable).
      6. Localization library is loaded.
      7. MU plugins run.
      8. Active plugins run.
      9. [plugins_loaded] action runs.
      10. Rewrite rules execute.
      11. Instantiate $wp and $wp_query.
      12. Setup the theme.
      13. Run the child theme functions.
      14. Run the parent theme functions.
      15. Run init.
      16. Setup widgets.
      17. Run wp().
      18. ⇒ Parse the HTTP request and handle any redirects. ⇐

Get started

For this example, pretend you own www.acmeinc.com and you want the following redirects:

From: www.acmeinc.com/about-us/
→ To: www.acmeinc.com/company/about/

From: www.acmeinc.com/products/roadrunner-traps/
→ To: www.acmeinc.com/discontinued-products/

From: www.acmeinc.com/return-policy/
→ To: www.acmeinc.com/really-there-are-no-returns-allowed/

If you have just a few redirects, it’s easier to just toss them into your VirtualHost config file. But if you have dozens, hundreds, or even thousands of redirects, RewriteMap is the tool for you.

Step 1: Map out your redirects.

First, make a list of all of the redirects you want to handle. Group them into these two groups:

Remember, RewriteMap only handles simple redirects.

Now, jump up to the top of this guide to continue implementing this cool feature!