Web

New toys, new troubles: Nginx, FastCGI and WordPress checklist

Before ‘the great harddrive fire of 2011’ we had FastCGI running on the server for some small projects. With everything else using Apache it was rather refreshing to see that there were faster, more lightweight alternatives.

Starting from a fresh slate, we’ve waved off the slow and clunky httpd for Nginx but in exchange for the shiner newer model there’s a bit of a learning curb in how the new kid does things. In addition, an upgrade in blog/CMS was in consideration – now things were running faster perhaps there was some forgiveness in using PHP and WordPress. My starting point was aged a little, so this post documents my time with installing and securing WordPress 3.2.1.

Step 1: Installing the dependencies

With Nginx and FastCGI installed, the necessities of WordPress dictate the required installs:

MySQL
A requirement for the base install. Easier to avoid complications with alternate databases (and these days it’s improved in performance)
PHP 5
For the latest (and probably most secure) versions of WordPress
MySQL module for PHP 5
You need to talk to the the database somehow..
FastCGI PHP init.d script
A great example is here
GD module for PHP 5
Generate multiple sizes of your assets (like thumbnails), save yourself the trouble and install it right away. For Debian/ubuntu: apt-get install php5-gd, which automatically replaces libgd2-noxpm with libgd2-xpm

Step 2: Securing your site.conf

Coming from a .htaccess perspective, there is a little work to the Nginx configuration. There are plenty of examples, but only recently I have come across a great article in creating a better config file.

It has been stressed that ‘if’s are evil but when used optimally it can produce a more secure environment for WordPress to run. In particular, the uploads directory is prone to executing rogue PHP files..

location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        include /etc/nginx/fastcgi_params;
        if ($uri !~ "^/path-to-wordpress/wp-content/uploads/") {
                fastcgi_pass 127.0.0.1:9000;
        }
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_intercept_errors on;
}

This snippet in your config means that any .php files are executed normally by PHP unless they are found in the WordPress uploads directory. Note that the above fastcgi_pass may differ if you are running PHP on a different port or if you are using a Unix domain socket.

Step 3: A 5 minute install with caution

There is an excellent set of guidelines in securing your install. Some fairly basic rules you can easily follow are:

  • Avoid having an administrator with user name admin
  • Set a non-default prefix for your WordPress tables
  • Ensure your passwords aren’t too short
  • If you can, obscure wp-admin with basic authentication

Installing in a sub-directory from root htdocs

I like to keep a website that isn’t completely run by WordPress. I also prefer an uncluttered htdocs, so I’ve kept my WordPress install in a sub-directory. However, with the instructions on integrating WordPress with your website failing to work with my setup as it kept causing 404 errors, I had to seek alternate methods.

If you still want the look and feel of your blog at root level without the clutter, just place a index.php file in your root htdocs with:

1
2
3
4
5
6
7
8
<?php
 
require_once('/PATH/TO/WORDPRESS/wp-load.php');
$themebase = get_theme_root().'/'.get_stylesheet();
query_posts(null);
require_once($themebase.'/index.php');
 
?>

This generates the index page of your WordPress install regardless of the subdirectory location.

If you want a slightly customized index, then you will need to deal with ‘The Loop’. I have used the structure of this to generate ‘splash pages’ that contain snippets of the last few stories.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
define('WP_USE_THEMES', false);
 
require_once('/PATH/TO/WORDPRESS/wp-load.php');
 
// Do header stuff here before posts.. can use get_header() to use the current theme header
 
// Start of 'The Loop'
// For this example, the top 10 entries are obtained
$posts = get_posts('numberposts=10');
foreach ($posts as $post) : start_wp(); ?>
 
// Do stuff with the individual posts here.. customize as needed
<?php the_date(); echo "<br />"; ?>
<?php the_title(); ?>
<?php the_excerpt(); ?>
 
<?php
endforeach;
// End of 'The Loop'
 
// Do footer stuff here after posts.. can use get_footer() to use the current theme footer
?>

Creating a unified look and feel for external pages

Notice the require pattern? If you wish to produce a unified look and feel by reusing the current theme’s header and footer, you can use the following (for most current themes that have separated their header/footer). A common use for such things are custom error pages.

1
2
3
4
5
6
7
8
9
10
11
<?php
define('WP_USE_THEMES', false);
 
require_once('/PATH/TO/WORDPRESS/wp-load.php');
get_header();
 
// Do your custom content here
 
get_sidebar();
get_footer();
?>
Standard