Author
Josh Stoik
Back-End Developer
Josh is a nerd. Ask Josh about his favorite terminal emulator! No, really, please ask him. Nobody ever asks him. Why doesn't anybody care about terminal emulators?
View all authors

WordPress Static Page Cache

WordPress is a dynamic application. When a page is requested, the application loads hundreds or thousands of files into memory, makes dozens or hundreds of database queries, and eventually, crafts a static HTML document, which the visitor’s web browser can then render as a web page.

All of this happens every time someone pulls up a web page.

As you might imagine, the complexity of such operations can put quite a strain on humble web servers. As traffic levels increase, the struggle to meet the demands can result in WordPress sites slowing to a crawl or worse, crashing.

Investments in better hardware will postpone the inevitable, but to really address the problem head on, one must eliminate the dynamic nature of WordPress.

By saving a static copy of the page being generated to the server, the chain of events must only occur once; after that, the server need only hand over the ready-made page when visitors request it.

This is the perfect solution for content-driven web sites like blogs and portfolios. When changes are made to the database (e.g. a new post is published, or an old one is edited), the author can simply clear the cache and the site is good as new!

This solution is not so good for interactive user sites like stores and forums, where access to realtime data or personalized templates are required. At the end of this article, we’ll examine some tricks that might still offer assistance for these types of sites.

W3 Total Cache

There are a lot of caching plugins available to WordPress, but none are as comprehensive as W3TC. It is available in both free and premium versions, however for users looking at just the static page caching functionality, the free version will suffice.

First things first, install it.

Once activated, you’ll find a “Performance” tab in the admin menu. Go to “Performance” > “General Settings” to enable page cache (to disk). Now go to “Performance” > “Page Cache” to configure settings specific to page caching. For most sites, the default settings will suffice. If you have any pages that need to be served dynamically, such as a contact form or a page displaying randomized content, you can add it to the list of “Never cache the following pages”.

That’s it!

You can verify page cache is working by looking at the source code of a page on your site. You should see something like the following at the end:

<!-- Performance optimized by W3 Total Cache. Learn more: http://www.w3-edge.com/wordpress-plugins/
Page Caching using disk: enhanced
Served from: brightbrightgreat.com @ 2015-07-11 10:17:07 by W3 Total Cache -->

If page cache is disabled for logged in users, you might instead see:


<!-- Performance optimized by W3 Total Cache. Learn more: http://www.w3-edge.com/wordpress-plugins/
Page Caching using disk: enhanced (User is logged in)
Served from: brightbrightgreat.com @ 2015-07-11 11:14:21 by W3 Total Cache -->

The (User is logged in) lets you know that page cache would be used, were it not for the fact that logged in users are specifically excluded from it.

W3TC has a lot of features beyond page cache that are worth checking out. “Minify” will attempt to compress static documents before saving them to disk, which can result in faster load times for users.

Minification can break things in unexpected ways, so if you enable it, carefully double-check that your site is still working as expected (you can use an Incognito/Private session to view the site as a regular visitor would see it without having to log out of your account). If your server is already gzipping requests, you probably won’t see substantial performance gains from Minify.

To completely clear your static page cache, click “Performance” > “Empty All Caches” in the admin toolbar. If you forget to do this, the cache will empty itself automatically eventually.

Advanced

The biggest disadvantage to using static page cache is that pages are, well, cached. If a web site allows users to log in and then shows them personalized content (e.g. “Welcome back, Jane!”), static page cache won’t work correctly; all visitors will receive the same static page. Either Jane won’t see her message, or everyone will see Jane’s message.

One possible workaround is to disable cache for logged in users, but allow it for everyone else. If your site users are simply low-privilege WordPress users (e.g. subscribers), this is the default behavior anyway. But cache can also be disabled by the presence of a cookie; if user sessions are controlled through custom code, set a cookie at login (and remove it at logout), and add the cookie name to “Rejected cookies” list.

For sites where the session-specific variation is minimal, it might be preferable to maintain static page cache for all users, and let Javascript make any necessary adjustments at runtime.

A good example of this would be a storefront that displays the current cart count in the toolbar. As items are added to the cart, the count could be written to a cookie, which Javascript could then read and plop into place. Highly specific pages like the shopping cart could be individually excluded from cache, ensuring they are always up-to-date.

Lastly, it might be necessary for sites to clear the page cache programmatically. For example, if a product page lists its availability, that figure should be adjusted when a new order is placed.

To do this, make a wrapper function like the following (add/remove caches as necessary), and include calls to it where needed:

function my_cache_clear(){
	//clear W3TC page cache
	if(function_exists('w3tc_pgcache_flush'))
		w3tc_pgcache_flush();

	//clear W3TC database cache (comment out if not using)
	if(function_exists('w3tc_dbcache_flush'))
		w3tc_dbcache_flush();

	//clear W3TC object cache
	if(function_exists('w3tc_objectcache_flush'))
		w3tc_objectcache_flush();

	//clear W3TC minify cache
	if(function_exists('w3tc_minify_flush'))
		w3tc_minify_flush();
}

These techniques are no substitute for good coding and asset optimization, but they can give your site a boost and help make sure pages load quickly and efficiently.

Posted By
Josh Stoik

How to Log MySQL Errors in WordPress

MySQL query errors can be difficult to diagnose and correct.

For performance reasons, most MySQL installations fail to retain an error log for posterity; instead, if an error occurs, the reason is passed back to the application during runtime. If that error isn’t captured then and there, it is lost forever.

Development Environments

WordPress contains a special development mode that can be enabled by adding WP_DEBUG, WP_DEBUG_LOG, and/or WP_DEBUG_DISPLAY constants to your configuration. When enabled, PHP errors/warnings/notices and MySQL errors will be logged to wp-content/debug.log and/or printed to the screen.

WordPress’s $wpdb object also provides some debugging functions in case you wanted to debug MySQL issues independently of PHP ones:

  1. $wpdb->show_errors(): this causes MySQL error information to be printed to the screen as it happens. You would call this function before the queries you are looking to debug.
  2. $wpdb->hide_errors(): this reverses the behavior of show_errors() and returns WordPress to its default configuration. You can call this function anytime after executing the questionable queries.
  3. $wpdb->print_error(): this prints the error, if any, from the most recent query.

Production Environments

These tools are probably all you need when developing a new theme or plugin, but you shouldn’t use these under production environments. For one thing, printing random, technical-looking strings in the middle of a document will break the layout and confuse your users. It can also provide interesting information to any bad actors who might be poking around your site. But even if you’re just logging the information, WP_DEBUG_LOG isn’t a great idea: it degrades your site performance and, under most server configurations, exposes way too much information to anyone who knows where to look.

Of course, by the time a site is live, you should have thoroughly debugged everything, so there’s no need to log query failures, right? Well… maybe.

There are a lot of ways to mess up a MySQL query. Chances are, no matter how many times you tested your code during development, you’ll have missed some highly obscure edge case. Even if you didn’t, and everything was coded perfectly, sometimes an update to the WordPress core can subtly change the way a query is structured.

Such a change occurred recently with the release of WordPress 4.4. In prior versions, Null values passed via $wpdb->insert() or the like were typecast according to the type specified. %s would convert a Null value to '', %d to 0, etc. Now, however, Null values are passed as-is to MySQL. For columns with NOT NULL attributes, this can create problems where previously none existed.

So what to do?

Though we were unable to find any documentation, investigation into the WordPress source code revealed that MySQL errors from the current page request are collected in an obscure global variable during runtime, $EZSQL_ERROR.

We can access this variable in a custom PHP function that we then trigger through one of WordPress’ action hooks. Since we want to capture all errors for a given page request, the shutdown action is the best candidate as it triggers just before PHP terminates.

The following example code block does just that. At the end of every WordPress page execution, the function looks to see if any MySQL errors were encountered. If there were any, it combines some basic runtime information (date, page, etc.) with the error details and emails it to the site administrator.

//-------------------------------------------------
// Database logging - query errors
//
// email database query errors to the contact
// specified
//
// @param n/a
// @return n/a

function db_debug_log(){

	//WP already stores query errors in this obscure
	//global variable, so we can see what we've ended
	//up with just before shutdown
	global $EZSQL_ERROR;

	try {
		//proceed if there were MySQL errors during runtime
		if(is_array($EZSQL_ERROR) && count($EZSQL_ERROR)) {
			//build a log entry
			$xout = array();

			//let's start with some environmental information
			$xout[] = "DATE: " . current_time('r');
			$xout[] = "SITE: " . site_url();
			$xout[] = "IP: " . $_SERVER['REMOTE_ADDR'];
			$xout[] = "UA: " . $_SERVER['HTTP_USER_AGENT'];
			$xout[] = "SCRIPT: " . $_SERVER['SCRIPT_NAME'];
			$xout[] = "REQUEST: " . $_SERVER['REQUEST_URI'];
			$xout[] = "\n\n\n\n";

			//and lastly, add the error messages with some line separations for readability
			foreach($EZSQL_ERROR AS $e) {
				$xout[] = str_repeat('-', 50) . "\n" . implode("\n", $e) . "\n" . str_repeat('-', 50);
				$xout[] = "\n\n\n\n";
			}

			//email it!
			//if a plugin overrides the content-type header for outbound emails, change the message body
			//below to nl2br(esc_html(implode("\n", $xout)))
			wp_mail(get_bloginfo('admin_email'), '[' . get_bloginfo('name') . '] DB Error', implode(“\n”, $xout));
		}
	} catch(Exception $e){ }

	return;
}
add_action('shutdown', 'db_debug_log');

If email isn’t desirable, whether for reasons of security or practicality, the general idea could be altered to push data via error_log() or write the contents to any arbitrary log file (preferably in a non-web-accessible location).

These techniques can help make hunting down elusive MySQL errors easier. With a proper record in place, developers can see what went wrong and where, and find a solution more quickly.

Posted By
Josh Stoik

How-To: Free SSL Certificates From Let’s Encrypt

SSL certificate costs have been steadily declining over the years. A decade ago, you’d have to shell out hundreds of dollars a year for a certificate, but more recently, adequate protection has been available from under $10.

Now, thanks to a non-profit initiative called Let’s Encrypt, SSL prices have truly bottomed out: free.

Let’s Encrypt operates a little different than traditional Certificate Authorities. Where historically certificate purchases have had to be authenticated through the domain’s registrant contact – a process that is all too often confusing for the client – Let’s Encrypt authenticates entirely through server-side software.

Let’s Encrypt is currently in beta, so there are still some kinks to work out, and a bit of elbow grease required for those wishing to give it a try. This article will help guide system admins through the setup process.

Installation

At the time of this posting, official distribution packages are mostly unavailable. So to get the source files, clone from git:

git clone https://github.com/letsencrypt/letsencrypt

Then run the installer:

./letsencrypt/letsencrypt-auto -h

Configuration

You can predefine settings in /etc/letsencrypt/cli.ini:

# This is an example of the kind of things you can do in a configuration file.
# All flags used by the client can be configured here. Run Let's Encrypt with
# "--help" to learn more about the available options.

# Use a 4096 bit RSA key instead of 2048
rsa-key-size = 4096

# Uncomment and update to register with the specified e-mail address
email = you@domain.com

# Uncomment to use a text interface instead of ncurses
text = True
agree-tos = True
renew-by-default = True

authenticator = webroot

Let’s Encrypt supports different authenticator methods for different server environments (e.g. Apache). We prefer “webroot”, which doesn’t mess with any server configuration files. This tutorial will assume you feel the same way.

Usage

To generate a certificate, run something like:

letsencrypt-auto certonly -w /path/to/webroot -d domain.com -d www.domain.com

This will generate a challenge directory at /path/to/webroot/.well-known. Your server will need to be configured to allow access to this hidden domain or the authentication will fail.

If all went well, you should now have a certificate! Certificates are stored in /etc/letsencrypt/archive/, but you’ll want to use the symlinks they generate in /etc/letsencrypt/live/ to ensure your host file is always pointing to the most recent version. An example nginx configuration looks like this:

server {
	...
	ssl_certificate /etc/letsencrypt/live/domain.com/fullchain.pem;
	ssl_certificate_key /etc/letsencrypt/live/domain.com/privkey.pem;
	...
}

After restarting your web server, you should now have luxurious SSL encryption.

Renewal

For security reasons, Let’s Encrypt certificates are only valid for 90 days, so at this stage, you’ll want to get something in place to help automate the renewal process. At the time of this writing, there are no helpers to do this, so we wrote our own. The following script will test certificates found in the /etc/letsencrypt/live/ directory and renew any that are expiring within the next 25 days. If adapting this for your server, be sure and update the letsencrypt-auto binary path.

#!/bin/bash
#
# USAGE:
# -q quiet



#--------------------------------------------------
# Default settings

today=$(date -d "now" +%s)
limit=25
updated=false
use_quiet=false
use_output(){
	#echo if we aren't being quiet
	if [ "$use_quiet" == false ]; then
		echo $1
	fi
}



#--------------------------------------------------
# Parse arguments

while [[ $# > 0 ]]
do
key="$1"

case $key in
	-q)
	use_quiet=true
	;;
	*)
		# unknown option
	;;
esac
shift # past argument or value
done



#--------------------------------------------------
# Renewal

for d in `find /etc/letsencrypt/live/ -mindepth 1 -type d`; do

	domain=`basename $d`
	use_output "------------------------------"
	use_output "DOMAIN: ${domain}"
	use_output "------------------------------"

	cert="${d}/cert.pem"
	exp=$(date -d "`openssl x509 -in $cert -text -noout|grep "Not After"|cut -c 25-`" +%s)
	days_expire=$(echo \( $exp - $today \) / 86400 | bc)
	use_output "	Expiring in ${days_expire} days."
	status="ignored"

	#under the limit, let's renew!
	if [ "$days_expire" -lt "$limit" ]; then
		cert_file="/etc/letsencrypt/renewal/$domain.conf"

		use_output "	Starting renewal..."

		domains=$(grep --only-matching --perl-regex "(?<=domains \= ).*" "${cert_file}")
		# Determine last character
		last_char=$(use_output "${domains}" | awk '{print substr($0,length,1)}')
		# If last character is comma, then delete it from $domains
		if [ "${last_char}" = "," ]; then
			domains=$(use_output "${domains}" |awk '{print substr($0, 1, length-1)}')
		fi

		webroot=$(grep --only-matching --perl-regex "(?<=webroot_path \= ).*" "${cert_file}")
		# Determine last character
		last_char=$(use_output "${webroot}" | awk '{print substr($0,length,1)}')
		# If last character is comma, then delete it from $webroot
		if [ "${last_char}" = "," ]; then
			webroot=$(use_output "${webroot}" |awk '{print substr($0, 1, length-1)}')
		fi

		if ! /path/to/letsencrypt-auto certonly --renew-by-default --config /etc/letsencrypt/cli.ini -w "${webroot}" -d "${domains}" > /var/log/letsencrypt/renew.log 2>&1 ; then
			use_output "	Renewal failed."
			status="failed"
		else
			updated=true
			use_output "	Renewal succeeded."
			status="renewed"
		fi
	fi

	use_output ""

	#log it
	echo `date +"%F:%T"`" ${domain} ${days_expire} ${status}" >> /var/log/letsencrypt-renew.log

done;



#--------------------------------------------------
# Restart server?

if [ "$updated" == true ]; then
	service nginx restart >/dev/null 2>&1
	use_output "NGINX restarted"
fi

With the renewal process automated like this, we can simplify maintenance and make these free SSL certificates practical on production environments.

Be sure to check out our tips on How to Achieve an A+ SSL Configuration on Nginx to see how to maximize your encryption potential.

View all BBG Technology posts here.

Posted By
Josh Stoik

WordPress Object Cache

It is not unusual for WordPress to run dozens or even hundreds (yikes!) of database queries when putting together a page. What’s worse, these queries aren’t the sort from children’s bedtime stories. They’re the dirty ones, full of temporary tables, JOINs on JOINs on JOINs, generic data types, and insufficient indexing. They’re the MySQL equivalent of The Wire.

As traffic loads increase, these computationally-heavy operations can quickly lead to a bottleneck, making your site sluggish or bringing it offline altogether.

To get a better idea of your own database use (or abuse), install a plugin like Query Monitor, which puts detailed query stats in the admin toolbar.

If you’re nearing the limit of what MySQL can handle, there are a few basic things you should consider doing:

  1. Go through your plugins and deactivate and delete anything that isn’t being used.
  2. Set reasonable post limits (10 or fewer) for archive pages.
  3. If you have a custom theme, try to minimize the number of secondary queries being run inside The Loop.

Beyond that, well, that’s the purpose of this article: Object Caching!

WordPress has the ability to cache query results (and other key/value data) in memory for later retrieval so it doesn’t have to pester MySQL on repeat requests.

This can dramatically reduce the load on MySQL, while maintaining comparable speed.

The main disadvantage of Object Cache is that objects are, well, cached. This means that changes made in the backend might not be immediately reflected on the front end.

This can be a deal breaker for applications with transactional data like e-commerce sites, unless the code contains strategic calls to wp_cache_flush(). But for content-driven sites like blogs and portfolios, it can be the perfect solution.

Object Cache also takes over handling of “transient” data, which otherwise get stored in the wp_options table. This will both speed up plugins and themes that rely on the transient API, and also reduce bloat in the database (there is no automatic garbage collection process run against wp_options, so data remains forever, and ever, and ever… (unless the same cache key is requested again)).

WordPress Object Caching is designed with extensibility in mind. All it needs is an API for key/value storage and retrieval.

There are innumerable candidates for the job, so let’s just focus on some of the most common:

XCache

XCache is my favorite PHP opcode cacher (opcode cache, incidentally, will also speed up the execution of your PHP scripts in general).

It is lightweight, scalable, and supports the latest versions of PHP.

To get started, install the XCache PHP extension (available in most Linux repositories), tweak the INI settings* (variable cache is the relevant feature here), and restart PHP.

After that, you can then enable Object Caching in WordPress by installing XCache Object Cache Backend or a more comprehensive caching solution like W3 Total Cache.

If you have multiple sites running on the same server, you need to ensure you are running version 3.0.3 or later.

The XCache extension comes bundled with admin scripts you can copy to your web root to get an idea of how cache is being utilized. You should install this after it has been running for a while to see if the memory allocations need to be raised or lowered (just make sure you restrict access, and delete it when you’re done!). Remember, the memory settings are per-process, so don’t get carried away.

APC

APC is another PHP opcode cacher, but it is no longer actively developed.

Unless you are running an older version of PHP or your hosting environment comes bundled with it, XCache is a better option.

To get started, install the APC PHP extension, tweak the INI settings, and restart PHP. You can install a standalone plugin like APC Object Cache Backend or, again, go with something like W3TC.

There remains, however, one excellent use case for APC: Facebook’s HHVM engine supports it out of the box! If you have already replaced PHP with HHVM, just install the APC Object Cache Backend plugin and you’re good to go.

Redis

Redis is a powerful key/value caching solution with support for multiple servers.

For complex web applications consisting of multiple servers, it is an ideal solution as the cache can be shared across more than one machine.

For more simple setups, it is a bit much, and noticeably slower than XCache or APC.

To get started, install the Redis server and PHP extension (if you are running HHVM instead of PHP, the “extension” is built-in). Once you have that configured, you can install the Redis Object Cache plugin.

Posted By
Josh Stoik

HHVM: The Gift That Keeps On Giving

Over the past 20 years, PHP has become the de facto server-side programming language of the web.

From leading open-source software like WordPress to some of the Internet’s most heavily-trafficked sites (Facebook, Twitter, and Wikipedia to name a few), you’ll find it everywhere.

But its developer-friendly syntax comes at a cost.

Before any Web Magic™ can happen, the human-readable PHP source code must be run through an interpreter. This process is expensive (particularly in terms of memory usage) and time-consuming. Ultimately, this sets an upper-bound on the number of simultaneous page requests a given server can handle. As a site nears this ceiling, it becomes necessary to either make the site more efficient, or upgrade the hardware.

For some sites, the answer is to sidestep PHP altogether and implement static page caching. But for others, where dynamic processing is unavoidable, the solution might lie in the interpreter itself.

As system administrators know, there is no shortage of PHP interpreters on the market.

Each has its own quirks, configurations, and compatibility issues, so there is no one-size-fits-all solution. But with a little experimentation and a lot of testing, performance gains can be substantial.

One of the most promising interpreters available today is the Hip-Hop Virtual Machine (HHVM). Developed in-house by Facebook, the name of the game for HHVM is speed. For one thing, HHVM meets the machine halfway, converting PHP source code into an intermediate bytecode (this bytecode, then, is what’s left to dynamically interpret at runtime).

This is similar to what PHP extensions like Xcache bring to the table. Another trick up its sleeves: HHVM uses a single multi-threaded process to handle requests (unlike, for example, PHP-FPM, which spawns multiple processes). This leads to much more stable CPU and memory usage for servers operating under heavy loads.

A couple months ago, one of the more resource-heavy sites managed by Bright Bright Great was suffering from a substantial increase in traffic. While publicity is one of the better problems to have, it did pose difficulties for the server, which simply wasn’t configured for that level of interest.

After some (read: lots of) compatibility testing, we successfully transitioned from PHP-FPM to HHVM. The results were astounding.

The average DOM-Ready time dropped by 46%. Simultaneous page serves increased by 112%. And CPU usage under load, once maxed out, remained stable at a healthy 55%.

In fact, the PHP gains were so successful the bottleneck was shifted to MySQL. After some code rewrites to help the site take advantage of object caching (via HHVM’s implementation of APC), the simultaneous page serving capacity was nearly doubled again.

But wait, there’s more!

As part of HHVM’s regular development cycle, its engineers are locked in a room with a surfeit of junk food and coffee (I imagine) and tasked to further optimize the code already in place. By focusing on improvements that directly benefit widely-used software, the real-world impact of any gains made is maximized. With the recent release of version 3.8, HHVM was able to serve 9.7% more pages per second for WordPress, 4.8% for MediaWiki, and 13.9% for Drupal8 (uncached).

We ran quick before and after tests ourselves (against the site previously mentioned) using the command line program siege. Sure enough, the upgrade was able to serve 4.3% more pages per second.

HHVM is indeed the gift that keeps on giving!

Posted By
Josh Stoik

How To Achieve an A+ SSL Configuration on Nginx

Qualys SSL Labs has a wonderful tool to help evaluate your server’s SSL configuration. I recommend you take a moment to scan your site and see how you fare. Go do that now. I’ll wait. If you didn’t get a perfect score, you aren’t alone.

The default configurations for popular server-side software like OpenSSL often support cryptographically weak standards that can put users at risk. Even major corporations have room for improvement.

Keep your server secure

If you’re on a shared server or at the mercy of control panel software like cPanel or Plesk, unfortunately there isn’t anything you can do. But if you have root access and a little knowing of the command-line, this article will help you harden your configuration so you can get a perfect A+.

While the following examples are specific to Nginx, they should be able to help point any readers running Apache or Lighttpd servers in the right direction.

Obtaining a Certificate

First, let’s start with a quick refresher on how you go about getting a certificate in the first place. Make a directory outside your web root, and once in that directory, generate a private key and certificate signing request (CSR). Your key should be at least 2048 bytes. In this example we’ll generate one that is 4096 bytes. If you have an existing key and can’t remember how strong it is, you can type:

openssl rsa -in domain.com.key -text -noout | head -n 1

To generate a new key, use:

mkdir /var/www/domain.com/ssl
cd /var/www/domain.com/ssl
openssl req -new -newkey rsa:4096 -nodes -keyout domain.com.key -out domain.com.csr

The openssl command will quiz you about your domain and organization. The Common Name (CN) needs to be the fully-qualified-domain-name for which you are purchasing the certificate. Remember, domain.com and www.domain.com are technically two different domains, so enter the CN exactly as visitors are expected to reach it. Some certificates, such as Comodo’s PositiveSSL, are magically valid for both www and non-www variants (aren’t they sweet?).

You should now find two files in your ssl directory: domain.com.key and domain.com.csr. That key is meant to be private, so take a moment to update its permissions. If 600 is too restrictive for your environment, 640 might do the trick.

chmod 0600 domain.com.key

Now go buy a certificate. Namecheap offers decent Comodo certificates from as low as $9, or you could spend more money elsewhere. It doesn’t much matter. As part of the certificate activation process, you’ll be asked for the CSR you just created, so keep that handy. Different Certificate Authorities (CA) have different validation processes, so just follow whatever instructions you’re given.

Once your certificate has been issued, upload them to the directory containing your key and CSR. If you are issued two or more bundled certificates (which is a common practice), they must be stitched together into a single file for Nginx. The order is important, so double-check the CA’s documentation. For Comodo PositiveSSL certificates, run the following:

cat domain.com.crt COMODORSADomainValidationSecureServerCA.crt COMODORSAAddTrustCA.crt AddTrustExternalCARoot.crt > domain.com.combined.crt

Nginx Set Up

By default, OpenSSL uses a weak 1024 byte key for Diffie Hellman key exchanges.  Let’s bump this up to 4096 by running the following command.  It can take a while to complete, so go make a sandwich:

openssl dhparam -out /etc/nginx/dhparams.pem 4096

Now let’s make Nginx’s SSL configuration a little more secure by adding the following code to the http{} block of your /etc/nginx/nginx.conf file:

http {
	...

	##
	# SSL Settings
	##

	## force modern protocols and ciphers
	## and enable ssl cache for a small
	## performance boost

	ssl_prefer_server_ciphers On;
	ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
	ssl_ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS;
	ssl_session_cache shared:ssl_session_cache:10m;
	ssl_dhparam /etc/nginx/dhparams.pem;

	...
}

Now that we have the general out of the way, let’s move onto site-specific configurations. Open the configuration file corresponding to your site, something like /etc/nginx/sites-enabled/domain.com.conf.

Let’s start by adding a server block to redirect www.domain.com to domain.com, and also force HTTPS connections for everything. If www.domain.com is your main domain, simply reverse the www and non-www in the following examples.

server {
	listen 80;
	listen [::]:80 ipv6only=on;

	server_name www.domain.com domain.com;

	rewrite ^(.*) https://domain.com$1 permanent;
}

Now in your main server block for the domain, add the following:

server {
	listen 443 ssl;
	listen [::]:443 ssl ipv6only=on;

	server_name domain.com www.domain.com;

	#point to the combined certificate and key we generated earlier.
	ssl_certificate /home/domain.com/ssl/domain.com.combined.crt;
	ssl_certificate_key /home/domain.com/ssl/domain.com.key;

	#enable HSTS (in supported browsers) to make sure all subsequent
	#user requests are done over HTTPS regardless of protocol typed.
	add_header Strict-Transport-Security "max-age=31536000";

	#redirect non-canonical domain over SSL.
	#this will only work if your SSL certificate also covers www.domain.com.
	if ($host = 'www.domain.com' ) {
		rewrite ^(.*)$ https://domain.com$1 permanent;
	}
	...
}

If your server is not configured for IPv6, remove the second listen lines from the above examples. Speaking of IPv6, Nginx only wants to see a single ipv6only=on attribute per port across all your server blocks. If you have additional server blocks defined, simply omit that string from their definitions.

That’s it! Restart Nginx to apply the changes:

service nginx restart

Last Thoughts

You should now have pretty darn good SSL support! But the fun doesn’t end here!

New threats or vulnerabilities can pop up any time, to say nothing of the inevitable march of progress (advances in technology will eventually make your once great setup laughably inadequate).  Make sure you regularly apply any security patches made available to your distribution.  You should also periodically rerun the Qualys SSL Labs scan to see if any tweaks are needed to stay on top.

Posted By
Josh Stoik

BBG Tips: Keep WordPress Tidy

WordPress is kind of magical. It gives site operators the power to quickly and effortlessly extend functionality, tweak design, and preview and post new content.

However, this power of immediate evolution – a key feature that has made so many insightful blogs about cats sleeping in weird places possible – can, over time, lead to clutter, increased hardware costs, and even security vulnerabilities.

Organized Desk

Keeping WordPress tidy is as important as keeping your workspace clean.

Thankfully, a little bit of spring cleaning will go a long way. (Even if you don’t get around to it until the end of summer.) There are two areas to focus on:

Disk

First and foremost, conduct a quick census of the plugins installed on your site. If you notice any that aren’t actually being used, deactivate and delete them. All plugins contribute to the overall resource demands made of your server, so at best, unused plugins are needlessly slowing things down.

More importantly, plugins also add a lot of code to your site, code which may contain bugs or security weaknesses that a hacker would love to leverage against you.

Themes are another area that can grow quite cluttered over time. Even though inactive themes won’t contribute one way or another to your site’s performance, you should still delete them from the server as they might contain exploitable security vulnerabilities.

Byte for byte, orphaned thumbnails from uploaded images comprise the most wasted disk space on the average aging blog.

Every time an image is uploaded to WordPress, anywhere from a handful to a boatload of differently-sized thumbnails are generated and saved to the server. The precise dimensions of the thumbnails vary from theme to theme, so for sites that have switched designs, this can lead to a lot of images cropped at resolutions that will never be accessed again.

The plugin Force Regenerate Thumbnails will take care of this, deleting all existing thumbnails, and regenerating only those that are actually required by the current theme. Depending on how many images you have, this can take a while, so go make a sandwich if you’re hungry.

Data

Your database, too, can easily start to look like the dark recesses of an attic, full of tangled and broken holiday decorations, old clothes, and haunted chests. The largest culprit here is usually post revisions.

WordPress maintains a history of all changes made to all posts.

This is nice because it allows you to review any changes you’ve ever made and rollback to a prior version at any time.  But this also generates a lot of bloat, particularly if you can’t decide between desert or dessert or desert or dessert.

Thankfully, this is something you can fix automatically by capping the number of revisions to some sane value. You can do this by adding the following to your wp-config.php file:

//specify the max # of revisions, 3 in this example
define('WP_POST_REVISIONS', 3);

The WP_POST_REVISIONS setting is not enforced retroactively. To remove or prune excessive revisions for existing posts, you can use a plugin like Thin Out Revisions.

The trash can also be an area of database bloat. When you delete a post, it is moved to the trash. Just like the trash on your laptop or the trash in your kitchen, items will linger forever and ever until someone bothers to empty it.

You can automate this task in WordPress, at least, by adding the following to wp-config.php:

//specify the # of days before emptying, 5 in this example
define('EMPTY_TRASH_DAYS', 5);

One last area of data bloat is one that can be really bad on some sites, and nonexistent on others: transient data.  WordPress includes an API for temporarily storing arbitrary key/value pairs for later retrieval.

Most often, transient data is used to cache results that would otherwise take a while to generate, such as a response from another server (for example, a Twitter feed) or the end result of a really convoluted query (like: find all posts published on a Wednesday about apples or oranges but not mangoes…).

By default, WordPress stores these data in the wp_options table.  Though each piece of information is given an expiration, WordPress does not do any automatic garbage collection.

Transient data is only removed if and when it is requested (after expiry).

If a given transient key is not reused, the data will outlive us all.  Fortunately, you can keep expired transient data in check with the help of the aptly named plugin Delete Expired Transients.

That’s it!  Happy cleaning!

Posted By
Josh Stoik