Protecting staging sites with Basic Authentication

This approach was primarily devised for use with WP Engine staging sites, but will work in any situation where you have no access to Apache’s config and need to use a single .htaccess file for multiple domains (e.g. a multisite setup).

We’ve recently had a couple of projects hosted on WPEngine where we need to have a staging site protected by Basic Authentication so only authorised users can access it. Normally I would configure Basic Authentication in the Apache VirtualHost, but on WP Engine (for example) they handle all the server configuration for us, meaning we don’t have access to the Apache configuration. WPEngine also provide a Git push to deploy facility, which we tend to take advantage of because Version Control Rools, OK? Because all the web files are within Git version control, we are using the same .htaccess file for the developer, staging and live sites. (We could Git ignore the .htacess file, but I prefer to version control more, rather than less.)

The approach we’ve taken is to put the code into the .htaccess file and to detect the domain of the staging site, and require them to enter the Basic Authentication username and password before proceeding. Let’s look at both stages of that process.

First we need to detect the domain we want to require authentication for, we do this using the SetEnvIf directive from Apache’s mod_setenvif module to examine the Host using a regex. If the Host matches the regex, then we set an Apache Environment Variable, which acts as a flag for a later conditional check. Here’s that directive:

# Detect the domain host and set a variable telling us to use auth
SetEnvIf Host staging.wpengine.com$ CFTP_USE_AUTH

Now we’ve set the flag we can check for it, using mod_access_compat, and issue a Deny Directive for all visitors with the Environment Variable flag set. Because we have initially set the Order to deny,allow, this Deny means nobody with this Environment Variable set is allowed to visit the site… we’ll take care of that later down the code. Here’s the conditional Deny directive:

# Detect the variable telling us to use auth and deny
Deny from env=CFTP_USE_AUTH

The next line uses the Satisfy directive to allow any visitors which pass one or other of the access requirements; in this case either they do not have the CFTP_USE_AUTH Environment Variable set (because the host does not match), or they have entered valid authentication details.

Satisfy any

Then there’s the Basic Authentication rules, which are fairly standard:

# Basic authentication
AuthType Basic
AuthName "Nothing to see here. Move along, please"
Require valid-user

…until you get the the location of the AuthUserFile, which is only worthy of mention in that you need to provide a path to it. I decided to generate the .htpasswd file on my development server and keep it in the document root, which is under version control, because Version Control Rools, OK? We do some jiggery pokery later to ensure that nobody can view the .htpasswd file over HTTP or HTTPS.

# N.B. This path will be specific to your server setup
AuthUserFile /the/path/to/the/document/root/.htpasswd

We use a Files directive to Deny access to any files called .htpasswd to anyone and everyone (note the Satisfy All, that’s important):

# Don't allow the .htpassword file to be shown to web users
<Files .htpasswd>
   Order allow,deny
   Deny from all
   Satisfy All 
</Files> 

The complete rules are below…

Order deny,allow

# Detect the domain host and set a variable telling us to use auth
SetEnvIf Host staging.wpengine.com$ CFTP_USE_AUTH

# Detect the variable telling us to use auth and deny
Deny from env=CFTP_USE_AUTH

Satisfy any

# Basic authentication
AuthType Basic
AuthName "Nothing to see here. Move along, please"
Require valid-user

# N.B. This path will be specific to your server setup
AuthUserFile /the/path/to/the/document/root/.htpasswd

# Don't allow the .htpassword file to be shown to web users
<Files .htpasswd>
   Order allow,deny
   Deny from all
   Satisfy All 
</Files> 

Just one more thing… what if you want to allow unrestricted access to some URLs in the site? To accomplish this you can identify them with more SetEnvIf directives, and set a different flag which allows the authentication to be bypassed. Here’s a set of rules which shows this in action:

Order deny,allow

# Detect the domain host and set a variable telling us to use auth
SetEnvIf Host staging.wpengine.com$ CFTP_USE_AUTH

# Detect some link structure and set a bypass variable
SetEnvIf Request_URI ^/whatever/[0-9]+/?$ CFTP_BYPASS_AUTH

# Detect the variable telling us to use auth and deny
Deny from env=CFTP_USE_AUTH

# Allow the redirects we've detected
Allow from env=CFTP_BYPASS_AUTH

Satisfy any

# Basic authentication
AuthType Basic
AuthName "Nothing to see here. Move along, please"
Require valid-user

# N.B. This path will be specific to your server setup
AuthUserFile /the/path/to/the/document/root/.htpasswd

# Don't allow the .htpassword file to be shown to web users
<Files .htpasswd>
   Order allow,deny
   Deny from all
   Satisfy All 
</Files> 

2 thoughts on “Protecting staging sites with Basic Authentication

  1. Jon Brown

    I had always assumed we couldn’t setup basic authentication on WPE, so this is great, thanks!

    One note, you might mention how to find /the/path/to/the/document/root/ as it’s a little foreign/tricky for most on WPE. I need to look at a few more WPE instances to confirm but I think on WPE this will always work:
    /nas/wp/www/sites/{instance_name}

    Side note: I’m lazy so I usually use ServerBuddy to run phpinfo() and find DOCUMENT_ROOT in the phpinfo() report.

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>