WordPress Correct File Permissions

To lock down the permissions on your WordPress install, from inside the WordPress site directory, run the below commands.

chown -R root:root .
chown -R nginx:nginx wp-content wp-admin/update.php wp-admin/update-core.php 
wp-admin/network/update.php wp-admin/network/update-core.php

Note, the above command assumes you are running under Nginx, if you are under Apache, please run the 2nd command replacing nginx with apache

The above command will change all the files in your WordPress root to the ROOT user, then change only the needed files back to user your web server is running as. This will allow you to update themes & plugins, and will also let you upload images, but you will not be able to update WordPress without changing the owner/group back to nginx on your WordPress root.

GoDaddy SSL Certificate with Nginx

Installing a SSL certificate from Go Daddy is a bit different then other providers.  With Go Daddy you must install a intermediate or chain certificate addition to your CA certificate. Nginx does not have a option, how Apache dose, for chain certificates. So to accomplish this, we look to the Nginx documentation:

If intermediate certificates should be specified in addition to a primary certificate, they should be specified in the same file in the following order: the primary certificate comes first, then the intermediate certificates.[ref]Nginx Module ngx_http_ssl_module[/ref]

What that means is this.  Download your CA from with your private certificate from Go Daddy.  Next download the gd_bundle.crt from https://certs.godaddy.com/anonymous/repository.seam.

After you have download the gd_bundle.crt file, copy it to the same directory on your Nginx server and run something similar to:

cat godaddy-ca.crt > godaddy-chain.crt && cat gd_bundle.crt >> godaddy-chain.crt

Now just add this new certificate to your nginx.conf per normal

ssl_certificate godaddy-chain.crt

Trac Installed on a Nginx Server

Over the last few weeks I have been converting all my different sites from Apache to Nginx. The nice part of this plan is as I move on from system to system, each change over get’s easier then the last.

This how-to will walk you threw how to instal Trac on a Nginx server. The plan here is to proxy your trac site back to tracd running server. Only proxy the files that are not real and can not be served by nginx alone.

To start out, install python and some python tools to your server

yum -y install python python-genshi python-setuptools 
subversion python-setuptools-devel

Now you need to download and install trac from svn

svn checkout http://svn.edgewall.org/repos/trac/trunk/ trac
cd trac

After you have the current version, compile and install trac

python ./setup.py install

This how-to assumes you already have a working trac profile to host on this site. If you do not already have a trac profile, please look at trac-admin /path/to/websites/directory initenv.

After you have trac installed, you need to start it.

export TRAC_ENV_INDEX_TEMPLATE=/var/www/trac/projects_list_template.html
/usr/bin/python /usr/bin/tracd -d -p 3050 
--auth=*,/var/www/trac.example.com/projects/.htpasswd,trac.example.com 
--protocol=http -e /var/www/trac.example.com/projects

And here’s a simple example Nginx config for your new trac setup.

   # trac.example.com
   upstream trac.example.com {
       server 127.0.0.1:3050;
   }
   
   server {
       listen 80;
       server_name trac.example.com;
       root /var/www/trac.example.com;
       
       location /html/ { 
       	expires 180d;
       }
       
       location /favicon.ico { }
       location /robots.txt { }
       
       location / {
               proxy_pass        http://trac.example.com;
               proxy_set_header  X-Real-IP  $remote_addr;
       }
   }

Custom 404 errors pages in Nginx

Here’s a quick how-to on creating a custom 404 error page on a Nginx server.

To display a single page for a site, add the below to your server’s config. The below config assumes /404.html is in the root of the current site.

server {
    ...
    error_page 404 /404.html;
    ...
}

The error page it self doesn’t have to be anything special, just a clear message for the user to know that this page dose not exist.

Gallery3 Installed on a Nginx Server

A few months ago I converted my web cluster from Apache to Nginx. Initially I was only concerned with my WordPress sites as they get, by far the most traffic. Their conversion went well, with only very minor issues.

Since I was happy with the results, I moved to quickly to my MediaWiki site. It’s conversion went very well and actually end up allowing the site to accept requests with and without index.php in them.

During this time, my other random sites, using odd or old software, was simply proxied back to the still running Apache install on my server. Using nginx’s proxy configuration, i was able to just change the port Apache listen on and left the old configurations as they were.  This allowed me to stay up, but didn’t give me any of the benefits of nginx.  Images from the my Gallery3 gallery were still being served from Apache, threw Nginx.

After getting everything working, I decided to start on Gallery3, since Gallery3 uses .htaccess files to secure images, I wasn’t sure how I would be able to go about this.  After some investigating I found that Gallery3 only uses the .htaccess files to block downloading of the actual images them self’s, the php pages are still secured threw normal permissions.

Configuring Nginx

Setting up Gallery3 under Nginx is pretty straightforward, when ignoring the .htaccess file talked about above.  As with all my Nginx setup’s, im using php-fpm via a UNIX sock.  Switching to a TCP connection (such as 127.0.0.0:8000), can be substituted with only minor tweaks.

To start out, create a new file in your nginx configuration directory, named gallery3.conf.  This file will be generic enough that you will be able to use it for any Gallery3 install on the server. Your site specific information will live in your main nginx.conf file.

Create a new file named gallery3.conf, and copy the below into it:

location ~* .(js|css|png|jpg|jpeg|gif|ico|ttf)$ {
    expires 180d;
#    if_modified_since off;
#    add_header Last-Modified "";
}

if (!-e $request_filename) {
    rewrite ^/(.+)$ /index.php?kohana_uri=$1 last;
}

location /var/ {
    try_files $uri /index.php?kohana_uri=$2;
}

location = /downloadalbum/zip/album/1 {
    return 404;
}

location  ~* .php$ {
    include fastcgi_params;
    fastcgi_index  index.php;
    fastcgi_split_path_info ^(.+.php)(.*)$;
    fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
    fastcgi_param  PATH_INFO        $fastcgi_path_info;
    fastcgi_pass php;

    index index.php;

    if (-f $request_filename) {
        rewrite ^/var/albums/(.*)$ /file_proxy/$1 last;
        expires max;
        break;
    }

    if (!-e $request_filename) {
        rewrite ^/(.+)$ /index.php?kohana_uri=$1 last;
    }
}

Now you need to setup your new site in Nginx’s main config file.  Below is the most basic setup, it assumes you have a running Nginx server and are only adding this site to it.  Note the name of the server and the root location of the files needs to be updated.

Added to nginx.conf:

server {
    listen 80;
    server_name gallery.example.com;
    root /var/www/gallery.example.com;
    include gallery3.conf;
}

This next part really sucks and I wish there was a way around it, in your Gallery3 installation, go into applications/config/config.php and modify the “index_page” setting as shown below.

application/config/config.php:

$config["index_page"] = "";

What sucks about this is you will need to do it after EVERY UPDATE. If after you update Gallery3 and you loose your style sheet and java scripts, this is why.

The config file should not live in a location that get’s updated automatically with new versions of software.

MediaWiki Installed on a Nginx Server

Here is my quick guide to use MediaWiki on a Nginx server using php-fpm.

There are many ways of doing this. I like pretty url’s, so this configuration will remove the index.php from the url. This is great, but it will not redirect links to index.php to the new none index.php links. You also need to configure MediaWiki to use short urls. Also, the below configuration dose assume your using Unix socket connections to php-fpm.

So here we go, inside your http { block, add the following.

/etc/nginx/nginx.conf:

    server {
        listen 80;
        server_name wiki.example.com;
        root /var/www/wiki.example.com;
        include mediawiki.conf;
    }

/etc/nginx/mediawiki.conf:

# MediaWiki Config
location ~ .htaccess {
        deny all;
}

location ~* /sitemaps/ {
        autoindex  on;
}

# Remote index.php from URI
rewrite ^/index.php/(.*) /$1  permanent;

location / {
        if (!-e $request_filename) {
                rewrite ^/([^?]*)(?:?(.*))? /index.php?title=$1&$2 last;
        }
        if ($uri ~* ".(ico|css|js|gif|jpe?g|png)(?[0-9]+)?$") {
                expires max;
                break;
        }
}

location ~* .php$ {
        if (!-e $request_filename) {
                return 404;
        }

        include /etc/nginx/fastcgi_params;

        fastcgi_pass  unix:/var/run/php-fpm.socket;
        fastcgi_index index.php;

        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
}

If you only have a single wiki on your server, there’s really no reason to create a new mediawiki.conf file, but instead just add it to the server block.

Installing WordPress on Nginx

I, like most people, started out by using Apache and really didn’t see anything wrong with it.  It’s relatively easy to setup, it’s used by most sites so support is a snap and a default and it’s already installed on most distributions. The thing is, it’s slow, the easy of use is paid for by speed.

So this is where Nginx comes in.  Nginx is not the easiest software to setup.  It requires you to tell it what different file types you will be using and how to handle them.  It requires you to tell it where scripts live and where static files live.  It also requires you to use an external php server, such as FastCGI.

PHP-FPM Setup

Nginx does not provide FastCGI for you (FastCGI is what your web server uses to interact with WordPress’s PHP code), so you’ve got to have a way to spawn your own FastCGI processes.

My preferred method is using of running FastCGI is with php-fpm.  Since I’m using Fedora, and there is a yum packet already built for php-fpm, it’s quick and easy to install.

Installing php-fpm is pretty straightforward:

yum install php-fpm

After installing php-fpm you have to start it. The rpm for php-fpm installs the service script for you, you only need to enable starting at boot, and start the process.

chkconfig php-fpm on
service php-fpm start

Nginx

The next part is to install Nginx on your server.  This is as straightforward as installing php-fpm on Fedora, when using yum.

yum install nginx

Once Nginx is installed, you need to set it up to server your site.

Configuring Nginx for WordPress

So we now have the needed software installed, next we need to set it all up. Below is the config for a standard, simple WordPress site named example.com.
nginx.conf:

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] $http_host "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;
    sendfile        on;
    #tcp_nopush     on;
    rewrite_log     on;
    keepalive_timeout  5;
    index              index.php index.html index.htm;

    # Upstream to abstract backend connection(s) for PHP.
    upstream php {
        server unix:/var/run/php-fpm.socket;
    }

    server {
        listen 80;
        server_name example.com;
        server_name www.example.com;
        root /var/www/example.com;

        if ($http_host != "example.com") {
                rewrite ^ http://example.com$request_uri permanent;
        }

        include wordpress.conf;
    }
}

wordpress.conf:

# WordPress single blog rules.
# Designed to be included in any server {} block.

# Deny all attempts to access hidden files such as .htaccess, .htpasswd, .DS_Store (Mac).
location ~ /. {
        deny all;
        access_log off;
        log_not_found off;
}

# Deny access to any files with a .php extension in the uploads directory
location ~* ^/wp-content/uploads/.*.php$ {
        deny all;
        access_log off;
        log_not_found off;
}

# Deny access to any files with a .php extension in the uploads directory for multisite
location ~* /files/(.*).php$ {
        deny all;
        access_log off;
        log_not_found off;
}

# This order might seem weird - this is attempted to match last if rules below fail.
# http://wiki.nginx.org/HttpCoreModule
location / {
        try_files $uri $uri/ /index.php?$args;
}

# Add trailing slash to */wp-admin requests.
rewrite /wp-admin$ $scheme://$host$uri/ permanent;

# Directives to send expires headers and turn off 404 error logging.
location ~* .(js|css|png|jpg|jpeg|gif|ico|ttf)$ {
        expires 180d;
        log_not_found off;
}

# Pass all .php files onto a php-fpm/php-fcgi server.
location ~ .php$ {
        # Zero-day exploit defense.
        # http://forum.nginx.org/read.php?2,88845,page=3
        # Won't work properly (404 error) if the file is not stored on this server, which is entirely possible with php-fpm/php-fcgi.
        # Comment the 'try_files' line out if you set up php-fpm/php-fcgi on another machine.  And then cross your fingers that you won't get hacked.
        try_files $uri =404;

        fastcgi_split_path_info ^(.+.php)(/.+)$;
        #NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini

        include fastcgi_params;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
#       fastcgi_intercept_errors on;
        fastcgi_pass php;
}