Securing WordPress Admin Via SSL Using a Separate Admin Subdomain

wordpress-logo-notext-rgb

Different WordPress and Site URLS

Our wordpress site has an SSL secured admin setup to protect our login credentials.   Since we do not have an SSL certificate for our main blog domain, but rather for secure.seriouslyproductions.com , the setup for WordPress to function properly was not standard and required some extra work.

mod_rewrite and Admin Pages

To accommodate this setup and ensure that the login page and all admin content was secured via SSL, I set the wordpress Address (in General Settings) to https://secure.seriouslyproductions.com.  The next step was to add a mod_rewrite rule to my .htaccess file to divert all traffic to https://secure.seriouslyproductions.com when attempting to access any admin page or the wp-login.php page.

If you are using Apache then adding the following lines to your .htaccess file should do the job.  These should be placed after the RewriteEngine has been turned on, but before the other rewrite rules that are in place for WordPress.  Naturally you would change out the https://secure.seriouslyproductions.com URL with your own website URL:

RewriteCond %{HTTPS} !=on [NC]
RewriteRule ^/?(wp-admin/|wp-login\.php) https://secure.seriouslyproductions.com%{REQUEST_URI} [R=301,QSA,L]

These lines added to the Basic WP htaccess file would look like:

# BEGIN WordPress

RewriteEngine On
RewriteBase /
RewriteCond %{HTTPS} !=on [NC]
RewriteRule ^/?(wp-admin/|wp-login\.php) https://secure.seriouslyproductions.com%{REQUEST_URI} [R=301,QSA,L]

RewriteRule ^index\.php$ – [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]

# END WordPress

PROBLEM: Unable to Preview Changes

At this point most things were working.  The login and admin pages were secure.  The only caveat was that I could not preview changes to any WordPress post or theme settings.  In addition, a logged in user would never see the WordPress admin bar at the top of the screen if viewing the blog URL.   By default, WordPress sends any preview request back over to the site URL (which in my case is set to http://www.seriouslyproductions.com/).  However, to preview a change a user must be logged in and have permissions to view the change.  This told me that the authentication cookie wasn’t available across the two domains (secure.seriouslyproductions.com and www.seriouslyproductions.com).

I wanted the preview functionality so I have been on a crusade to determine a way to enable this functionality with my cross subdomain setup.  In this earlier blog post I outlined a quick “fix” in which I used mod_rewrite rules that diverted preview post content over to my secured subdomain, https://secure.seriouslyproductions.com.  This solution worked for previewing posts and page updates, but failed miserably for previewing my custom theme updates.  In addition, this solution gave me SSL warnings because WordPress was serving insecure content over the SSL channel (images and other content) when previewing changes.  Clearly this was only a temporary solution, but it worked well enough for the time being.

SOLUTION: Modifying the Cookie Domain

Over the past few weeks, and as time permits, I’ve been doing research and I believe I’ve found the solution to my problem.  To start, I removed the “hack” fix that I put in place which diverts preview traffic over to secure.seriouslyproductions.com from the .htaccess file.

Now for some explanation…  By default, WordPress authentication cookies are only recognized for the domain entered into the WordPress Address (in my case secure.seriouslyproductions.com).  Any visit to www.seriouslyproductions.com will not acknowledge that a user is logged in as it cannot find a “logged in” cookie for the www subdomain.  This results in any request to preview content to fail.

The best solution I’ve found is to alter the cookie domain of the WordPress logged in cookie (NOT the auth cookie which is a security risk) to the value of my root domain without a subdomain, i.e “seriouslyproductions.com”.  This makes the cookie available across all subdomains of seriouslyproductions.com and now all is well in the world.  The code where all authentication cookies are set can be found in the wp-includes/pluggable.php file and is easy to modify.

  1. function wp_set_auth_cookie(…):  The cookies are set toward the end of this function.  There are two lines related to the LOGGED_IN_COOKIE that you need to locate.  In those lines, replace COOKIE_DOMAIN with a string literal containing your base domain name (e.g. change COOKIE_DOMAIN to “yourdomain.com”).  DO NOT change the COOKIE_DOMAIN for the auth cookie.

    Change this:
    setcookie(LOGGED_IN_COOKIE, $logged_in_cookie, $expire, COOKIEPATH, COOKIE_DOMAIN, $secure_logged_in_cookie, true);
    if ( COOKIEPATH != SITECOOKIEPATH )
         setcookie(LOGGED_IN_COOKIE, $logged_in_cookie, $expire, SITECOOKIEPATH, COOKIE_DOMAIN, $secure_logged_in_cookie, true);

    To this:
    setcookie(LOGGED_IN_COOKIE, $logged_in_cookie, $expire, COOKIEPATH, "yourdomain.com", $secure_logged_in_cookie, true);
    if ( COOKIEPATH != SITECOOKIEPATH )
         setcookie(LOGGED_IN_COOKIE, $logged_in_cookie, $expire, SITECOOKIEPATH, "yourdomain.com", $secure_logged_in_cookie, true);

  2. function wp_clear_auth_cookie(…):  In the first block of setcookie calls you will see two lines for the LOGGED_IN_COOKIE again.  As in step 1, replace COOKIE_DOMAIN with your base domain name.

    Change this:
    setcookie( LOGGED_IN_COOKIE, ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN);
    setcookie( LOGGED_IN_COOKIE, ' ', time() - YEAR_IN_SECONDS, SITECOOKIEPATH, COOKIE_DOMAIN);

    To this:
    setcookie( LOGGED_IN_COOKIE, ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, "yourdomain.com");
    setcookie( LOGGED_IN_COOKIE, ' ', time() - YEAR_IN_SECONDS, SITECOOKIEPATH, "yourdomain.com");

Once I updated the wordpress logged in cookie’s domain, all my previews work properly and the WordPress install is working like a champ.  I can see all preview content, both posts and theme updates, and I don’t receive any SSL warnings when previewing content.  The only down-side to this type of setup is that you must reapply the change every time WordPress updates the wp-includes/pluggable.php file.  If you have a code repository such as Git, then this isn’t such a big deal, but it’s something that is easy to forget if kept track of manually.

If you’ve benefited from this post and it saved you time, consider sending me a tip via PayPal.



Leave a Reply