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> 

Join the Conversation

8 Comments

  1. 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.

  2. WPE recently added htpasswd protection to the dashboard but you can’t set your own password which makes it a bit frustrating. My fingers are crossed the add that ability soon. http://wpengine.com/2015/01/06/announcing-password-protection-sites-development/

    What’s also frustrating is I _think_ they’re putting the htpasswd file up a directory so you can’t edit it manually to add your own user/pass combos. Hence I’m still using this method.

    One frustration with this method though is that if .htpasswd doesn’t exist the server 500’s. For many that’s probably a better option than displaying the site, but wondering if there is any htaccess magic to make that path relative or to check the file exists?

    This is what we use currently:
    https://gist.github.com/9a8c978abc0dab7e5344

  3. Hi

    I have always a 500 Internal Server Error because the Path/File of the AuthUserFile dos not exist on my local webserver!

    Do i anything Wrong or must i have identical Path’s with your solution?

    1. You’ll need to work out what the path is for your site, which you can do by uploading a file which does something like: echo __FILE__; (this will give you the path to your uploaded file, which you can use to work out the path to your password file).

      1. Because I’m lazy i often install the plugin Server Buddy which includes a button for outputting phpinfo() and will get you the path as well.

        I’ve long wished there was a if file_exists() conditional available on .htaccess.

    2. It was just a simple Error on my side!
      The Hoster don’t support “SetEnvIf” in htaccess :-(

Leave a comment

Leave a Reply to John K Cancel reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.