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:

Scenario A

If you’re mapping redirects from the same domain, simply provide source/destination URIs in ~/redirects.txt

# Comments are OK
/about-us/ /company/about/
/products/roadrunner-traps/ /discontinued-products/
/return-policy/ /really-there-are-no-returns-allowed/

Scenario B

If you have a WP multisite install, you’ll need to specify the full URL for each redirect, otherwise the redirects you set will apply to all sites in your multisite collection. Leave HTTP/HTTPS off for the source URLs, but use them for the destination URLs to eliminate HTTP→HTTPS redirects.

# Comments are OK
www.acmeinc.com/about-us/ https://www.acmeinc.com/company/about/
www.acmeinc.com/products/roadrunner-traps/ https://www.acmeinc.com/discontinued-products/
www.acmeinc.com/return-policy/ https://www.acmeinc.com/really-there-are-no-returns-allowed/

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

Edit /etc/apache2/sites-available/acmeinc.conf and update the VirtualHost to include the new mapping file.

<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:

  • Simple redirects, like /a//b/.
  • Complex redirects with wildcards or regular expressions, like /c/(.*)/d/$1.

Remember, RewriteMap only handles simple redirects.

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