nginx, PHP, and WordPress (migrating from lighttpd)

Though lighttpd has served me well for the last couple years, it’s time to switch.

lighttpd 1.5.0 has been in the works since at least 2006 and doesn’t look like it’ll be out anytime before Duke Nukem Forever.

nginx, on the other hand, is leading the pack of extremely resource-efficient C10k-answering web servers these days.

So let’s jump into a CentOS/RHEL install. First, download the latest stable version and your desired modules. Unzip it all and issue your typical commands. Here are mine.

./configure --with-http_ssl_module --with-http_gzip_static_module --add-module=/root/ngx/mod_zip-1.1.5 --add-module=/root/ngx/nginx-accesskey-2.0.3 --add-module=/root/ngx/ngx_http_secure_download --add-module=/root/ngx/nginx_upload_module-2.0.10 --add-module=/root/ngx/masterzen-nginx-upload-progress-module-2bf172d
sudo make install

I chose a bunch of modules, but they’re all optional. You probably want the HTTP SSL module and the upload module (without which you can’t accept file uploads or multipart forms?!). Secure download is particularly useful to me for Sendshack, allowing for download links.

If you have problems with ./configure, make sure you’re using a stable release. Trying to configure 0.8 wouldn’t work with many of the modules I selected.

Let’s set up PHP first. We’ll be running PHP via FastCGI, so make sure your PHP build supports it (you should see cgi-fcgi if you type php -v at the command line).

Make a file named fastcgi.conf in your nginx conf directory (default /usr/local/nginx/conf) that dictates what variables nginx passes to PHP. It should contain this:

fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param QUERY_STRING $query_string;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param CONTENT_TYPE $content_type;
fastcgi_param CONTENT_LENGTH $content_length;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param DOCUMENT_URI $document_uri;
fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_param SERVER_PROTOCOL $server_protocol;
fastcgi_param GATEWAY_INTERFACE CGI/1.1;
fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
fastcgi_param REMOTE_ADDR $remote_addr;
fastcgi_param REMOTE_PORT $remote_port;
fastcgi_param SERVER_ADDR $server_addr;
fastcgi_param SERVER_PORT $server_port;
fastcgi_param SERVER_NAME $server_name;
# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param REDIRECT_STATUS 200;
fastcgi_index index.php;

Reference it in your nginx.conf — your http section should begin:

http {
include /usr/local/nginx/conf/mime.types;
include /usr/local/nginx/conf/proxy.conf;
include /usr/local/nginx/conf/fastcgi.conf;

Next, make sure you have spawn-fcgi installed. If you don’t, a quick yum install spawn-fcgi should take care of it.

Here’s where I bring something different to the table. Most people will tell you to set up a TCP socket for PHP, but I run a tight iptables ship and think it inelegant to bounce around TCP ports, so I brought my lighttpd Unix socket config over to nginx.

Edit /etc/init.d/nginx and change your start and stop functions to:

start() {
[ -x $nginx ] || exit 5
[ -f $NGINX_CONF_FILE ] || exit 6
echo -n $"Starting $prog: "
daemon $nginx -c $NGINX_CONF_FILE
/usr/local/bin/spawn-fcgi -s /tmp/php-fastcgi.sock -f /usr/bin/php-cgi -u ngninxuser -g nginxgroup -C 5 -P /var/run/
[ $retval -eq 0 ] && touch $lockfile
return $retval
stop() {
echo -n $"Stopping $prog: "
killproc $prog -QUIT
killproc php-cgi
[ $retval -eq 0 ] && rm -f $lockfile
[ -f /tmp/php-fastcgi.sock ] && /bin/rm -f /tmp/php-fastcgi.sock || :
[ -f /var/run/ ] && /bin/rm -f /var/run/ || :
return $retval

Substitute your nginx user and group for the placeholders nginxuser and nginxgroup.

Now you can set up a virtual server. nginx doesn’t, but should, allow some global (for me) directives to be set across all servers, so include them by referencing a file to allow for easier editing.

Make a file called vs_std.conf that contains:

location / {
index index.html index.htm index.php;
location ~ \.php$ {
fastcgi_pass unix:/tmp/php-fastcgi.sock;
location ~* \.(jpg|jpeg|gif|png|swf)$ {
access_log off;
expires 7d;

You may or may not want to include an expires directive like the last location item. Purely optional.

Then reference that file from the server declaration in your config file.

server {
listen 80;
root /home/;
include vs_std.conf;

You can also enable gzip for text by including this in the http section:

gzip on;
gzip_types text/html text/css text/xml application/x-javascript application/atom+xml text/mathml text/plain text/ text/vnd.wap.wml text/x-component;
gzip_disable "MSIE [1-6]\.(?!.*SV1)";

The disable line is particularly important if you’re worried about IE compatibility, since gzipped content breaks anything before IE6 SV2.

For WordPress, the easiest way to manage permalinks is to route 404s to WordPress. Then you can create whatever outlandish permalink scheme you like and WordPress will happily recognize the request URI and return your golden post.

Here’s my configuration entry for this blog.

server {
listen 80;
root /home/;
if ($host ~* "www") {
rewrite ^/(.*)$$1 permanent;
if (-f $request_filename) {
if (!-e $request_filename) {
rewrite ^.*$ /index.php last;
include /usr/local/nginx/conf/vs_std.conf;

That about does it. Let me know if I’ve omitted anything or if you have any questions.