Migrate your Apache server to nginx on CentOS

How do you migrate an existing Apache server, to a brand new nginx installation for several websites that use PHP? This is a simple tutorial into changing an Apache installation into a nginx one, without having to change your existing websites.

nginx is a server that scales far better compared to apache running on the same hardware. The tutorial is not super CentOS specific, but all the commands were run on a CentOS.

The Apache server that was migrated, namely this blog, has several virtual hosts, that are all running PHP, some of them Joomla websites. The plan is to take them as they are, and have them available externally the same way as before, using the same virtual host names, the same folder locations, with the same users assigned to them.

The reason is that if we screw up something in the process, we can just revert to our old proven Apache, by just restarting the Apache service and shutting down nginx. Also we can minimize the downtime, since if done right it should be in the end just shutting down apache and starting nginx, but if it doesn't work we can quickly go back to serving the files with Apache until we figure out what is going on.

While it is simple, it is a pretty long read, so grab your coffee, and hack away:

1. Install nginx

This is as simple as running:

yum install nginx

Make sure the /etc/nginx/conf.d/default.conf has the paths pointing to /var/www/html, or whatever was the default site for your Apache configuration. (In my case it was /var/www/blog).

OK, next

2. Enable PHP processing

Unlike Apache, the PHP processing does not take place in the same process as nginx via modules, since nginx doesn't supports modules except at compile time. That's why having a service to do the PHP rendering is required.

Since I already have PHP installed for my apache installation, most of the packages are already installed, so I only needed the FastCGI integration to get it running under nginx.

yum install php55w-fpm

If you're doing a new installation you would need to fetch also cli and common packages, and whatever other libraries you would need (like gd or whatever else your web application requires).

Now let's add the PHP processing via php-fpm. They should look something like this:

Don't just uncomment the configuration that is there, since it will be prone to attacks, and it won't work out of the box either.

Furthermore since Joomla uses a pretty messed up path segment processing to pass its arguments (e.g. /index.php/blog/96-sun-misc that Joomla uses vs /index.php?category=blog&article=96-sun-misc), you will need to add another rule into the root location of your server:

Now because we're rewriting the URLs we also need to take special care for our Joomla sites that we don't execute php files from writable folders. Thus we need to add after the root location the following:

So in order to add them as well to nginx, the following changes are needed to be added into a new file named /etc/nginx/conf.d/gzip_compression.conf, and also having the new font mimetypes registered into the existing /etc/nginx/mime.types.

mime.types

Simply add the mime types, basically the AddType statements from the previous configuration, before the closing bracket into the /etc/nginx/mime.types file.

font/truetype ttf;font/opentype otf;

Also make sure that the js files are mapped to the text/javascript mimetype, since by default in nginx they are mapped for some reason to application/x-javascript.

text/javascript js;

gzip_compression.conf

Enable the gzip compression, and register it for our mime types. Note that text/html is registered by default, and you will get a warning if you add it into the gzip_types.

gzip on;gzip_proxied any;# text/html is registered by default into the gzip_types, and you will get a warning adding it again here.gzip_types application/json text/javascript text/css text/plain font/truetype font/opentype;gzip_vary on;gzip_disable "MSIE [1-6]\.(?!.*SV1)";

Restart nginx for changes to take effect.

5.2 Use Local Sockets for php-fgm

If you noticed at step 2, in order to get our PHP running, the nginx server was talking via the loopback device to the php-fpm daemon on port 9000. Since we don't want to transport everything via TCP/IP for no good reason, we'll use local sockets that should be slightly faster in establishing the connections, and actual serialization of data.

Our local socket will be located at: /var/run/php-fpm/php-fpm.socket.

Thus in the /etc/php-fpm.d/www.conf file, we need to change the listen from:

The reason for the other lines is that by default, because of reasons, php-fpm will create the socket with the owner root, despite the fact that the actual runner instances for this pool will run as apache (see step 3). And we want a socket with just enough rights that can be read by nginx.

Thus we will change in both /etc/nginx/conf.d/default.conf the fastcgi_pass to:

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

6. Virtual Hosts

Now for each virtual host basically the same configuration takes place, but only with the location configuration sections for the root and the PHP processing. For example this is what I use for the codeeditor.ciplogic.com:

7. Make it permanent

Since all seems fine now, the last thing we need to do is to ensure that when rebooting the nginx with php-fpm will start up instead of apache. Thus we need to disable the old httpd service and enable both nginx and php-fpm.

chkconfig httpd offchkconfig nginx onchkconfig php-fpm on

Congratulations! You're done.

Final Note

Performance wise the speed increase was relatively marginal, about 5% better for fetching the php files. Considering that I didn't changed the PHP version, nor upgraded Joomla, or did anything else, that alone sounds amazing, but not only it was faster, also the memory usage dropped in half, from 240MB under apache to 130MB using nginx + php-fpm.