How I migrated from Apache to Nginx

Published on: September 5, 2015

It's no secret that nginx has certain advantages over apache. One of them is that nginx is supposed to have better options for forwarding requests to ports other than port 80. My VPS has been using apache ever since I set it up because at the time apache was the only server I knew how to install and set up. But, as I learned more and wanted to start using different ports for node.js or python apps, I figured that I needed to move over to nginx. And so I did. In this post I will describe how.

Preparing

When I started the process of migrating I made sure that I had a backup of my most important website files. Not that I expected my files to blow up somehow, I just wanted to make sure that I wouldn't lose anything. Fortunately I have most of my projects in gitlab so that wasn't really an issue. After that I downloaded a back-up of my blog's database. Again, just to be sure.

After I reassured my paranoid mind that everything would be fine I went on to install the things I needed to start migrating. There were only two things I really needed, nginx and php5-fpm. The nginx package is needed to launch the nginx server and php5-fpm is what nginx will hand php files off to. Installing them took just two simple commands.

sudo apt-get install nginx
sudo apt-get install php5-fpm

Configuration

Before I was able to make nginx serve my websites I had to configure php5-fpm so it doesn't serve files based on a best guess, but only if we have an explicit (valid) file path. Even though this sounds like something that would make a great default I had to set that myself. In order to make this happen I had to modify the /etc/php5/fpm/php.ini file. This is the line that I had to change:

cgi.fix_pathinfo=0

If the line is set like that it's good. That's the secure config we're looking for. Next I had to set up the socket that php-fpm and nginx will use to communicate. The configuration for that is in /etc/php5/fpm/pool.d/www.conf and the line where the listening is configured should look like this.

listen = /var/run/php5-fpm.sock

Once this is set, php-fpm need to be restarted for the changes to take effect.

sudo service php5-fpm restart

Setting up nginx

After setting up php-fpm it was time to start setting up my websites. Just like apache, nginx can have multiple websites configured. Translating a basic website is not very hard, it took me something like 30 minutes to figure out how I could move Arto from an apache to an nginx config. A virtual host in apache might look something like below.

<VirtualHost *:80>
    ServerAdmin webmaster@localhost
    ServerAlias www.artoapp.nl
    ServerAlias artoapp.nl  

    DocumentRoot /var/www/www.artoapp.nl
    <Directory />
        Options FollowSymLinks
        AllowOverride all
    </Directory>
    <Directory /var/www/www.artoapp.nl/>
        Options Indexes FollowSymLinks MultiViews
        AllowOverride all
        Order allow,deny
        allow from all
    </Directory>

    ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
    <Directory "/usr/lib/cgi-bin">
        AllowOverride None
        Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
        Order allow,deny
        Allow from all
    </Directory>

    ErrorLog ${APACHE_LOG_DIR}/error.log

    # Possible values include: debug, info, notice, warn, error, crit,
    # alert, emerg.
    LogLevel warn

    CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>

Translating this to an nginx configuration looks like the following snippet.

server {
    listen 80;

    root /path/to/www.artoapp.nl;
    server_name artoapp.nl www.artoapp.nl;
    index index.html;

    location / {
        try_files $uri /index.html;
    }

    location /api/ {
        try_files $uri /api/index.php;
    }

    location /cms/ {
        try_files $uri /cms/index.php;
    }

    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass unix:/var/run/php5-fpm.sock;
        fastcgi_index /index.php;
        include fastcgi_params;
    }

    location = /robots.txt {
        allow all;
        log_not_found off;
        access_log off;
    }

    location ~ /\. {
        deny all;
    }

    location ~* /(?:uploads|files)/.*\.php$ {
    deny all;
    }
}

What you might notice here is that the nginx configuration is a little bit longer and more detailed than the one for apache. This is because apache uses .htaccess files to set up global or per folder access and rewrite rules. Nginx doesn't use these files and instead the configuration for that goes into your server configuration.

The first part tells nginx on which port it should listen. This makes it easy to spawn servers on different ports. Or to have multiple domains on a single port. The next three lines tell nginx what the full path to the website is, what server name it should use and which file should be used as the index. The first two properties can also be found in the site configuration for apache. The last one, index is not in the apache config. It tells nginx which file is the index file for the website.

Next up:

location / {
    try_files $uri /index.html;
}

location /api/ {
    try_files $uri /api/index.php;
}

location /cms/ {
    try_files $uri /cms/index.php;
}

This section is similar to what the .htaccess would do. It tells nginx how it should attempt to serve files for certain paths. So if we're at /, it tries to serve the requested files. If that's not possible, we get index.html. If we request something from /api/ it tries to serve the requested path as a file, if that fails we get sent through to index.php file. The /cms/ section works just like the /api/ section, just a different path.

The part after that are intended to send files through to php-fpm if the requested file is a php file. Next there's a special declaration for robots.txt. Finally we deny access to all files that start with . because those files are not supposed to be accessed and the final block denies execution of php in the uploads and files folder.

Making the switch

After translating Arto I had to convert a bunch of other websites with very similar configurations. When I was done with that it was time to make the switch. All it took to migrate after setting up my configuration was to stop apache (sudo service apache2 stop) and start nginx (sudo service nginx start or sudo service nginx restart if nginx was already running for some reason).

I expected things to break, fall over and not work because migrating just couldn't be this easy. But, in fact, it was. I only had one issue with a php extension I use, it wasn't enabled on php-fpm yet. Enabling it was all it took to get everything up and running. Next up, building awesome node.js and python websites instead of using php! If you're looking for a more complete guide of what you can do with nginx, they have great docs over at nginx.org.

If you have questions about this article or if you've got feedback for me, you're always welcome to shoot me a tweet.

Categories

Uncategorized

Subscribe to my newsletter