At the end of last year, this blog was migrated to a new server, hosted by DigitalOcean. The old server ran on Apache but on the new server I wanted to try out the LEMP One-click application.

LEMP stands for Linux, NGINX (say: en-juhn-ex), MySQL, PHP. The old server ran on Apache, so some things needed to be migrated from Apache to Nginx.

Differences between Apache and NGINX

The biggest difference between Apache and NGINX (for me) is the fact that Apache can be configured using .htaccess files and NGINX can't. That means that any logic contained in the .htaccess file(s) must be migrated to the NGINX Server Block. A server block is the NGINX equivalent of Apache's Virtual Host.

If you want to learn more, read this excellent article about the differences between Apache and Nginx.

The old .htaccess file

So, let's have a look at the old annotated .htaccess file. There was only one file, which was located in the public_html directory.

<IfModule mod_rewrite.c> <IfModule mod_negotiation.c> Options -MultiViews </IfModule> # set Expire and Cache Control headers for css and js <IfModule mod_expires.c> ExpiresActive On ExpiresDefault "access" ExpiresByType text/css "access plus 1 year" ExpiresByType application/javascript "access plus 1 year" ExpiresByType font/truetype "access plus 1 year" ExpiresByType font/opentype "access plus 1 year" ExpiresByType application/x-font-woff "access plus 1 year" ExpiresByType image/svg+xml "access plus 1 year" ExpiresByType application/vnd.ms-fontobject "access plus 1 year" ExpiresByType image/vnd.microsoft.icon "access plus 1 month" </IfModule> RewriteEngine On # Redirect to preferred domain RewriteCond %{HTTP_HOST} !(^barryvanveen\.test|^barryvanveen\.nl)$ [NC] RewriteRule ^(.*)$ https://barryvanveen.nl/$1 [R=301,L] # Redirect old Dutch urls to English urls RewriteRule ^over-mij$ https://barryvanveen.nl/about-me [L,R=301] RewriteRule ^over-mij/boeken-die-ik-heb-gelezen$ https://barryvanveen.nl/about-me/books-that-i-have-read [L,R=301] # Redirect to HTTPS domain RewriteCond %{HTTP_HOST} ^barryvanveen.nl$ [NC] RewriteCond %{HTTPS} !=on [NC] RewriteRule ^(.*)$ https://barryvanveen.nl/$1 [R=301,L] # Redirect assets with filehash in name to actual filename RewriteRule ^dist/css/(.*)\.[0-9a-f]{8}\.css$ /dist/css/$1.css [L] RewriteRule ^dist/js/(.*)\.[0-9a-f]{8}\.js$ /dist/js/$1.js [L] # Remove trailing slashes if not a folder RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_URI} (.+)/$ RewriteRule ^ %1 [L,R=301] # Handle request using index.php RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^ index.php [L] </IfModule>

The equivalent NGINX Server Block

And here is the NGINX Server Block that accomplices the same thing.

# map content types to expire times map $sent_http_content_type $expires { default off; text/css max; application/font-woff max; application/javascript max; ~image/ 7d; } # redirect http to https server { listen 80; listen [::]:80; server_name barryvanveen.nl; server_name www.barryvanveen.nl; return 301 https://barryvanveen.nl$request_uri; } # redirect https to domain without www. server { listen 443 ssl; listen [::]:443 ssl; server_name www.barryvanveen.nl; return 301 https://barryvanveen.nl$request_uri; ssl_certificate /path/to/certificate.pem; # managed by Certbot ssl_certificate_key /path/to/private/key.pem; # managed by Certbot } # serve website over https server { listen 443 ssl; listen [::]:443 ssl; server_name barryvanveen.nl; root /var/www/barryvanveen_nl/public_html; index index.php index.html index.htm index.nginx-debian.html; # serve website using index.php location / { try_files $uri $uri/ /index.php?$query_string; } # redirect old Dutch urls to English urls location = /over-mij { return 301 /about-me; } location = /over-mij/boeken-die-ik-heb-gelezen { return 301 /about-me/books-that-i-have-read; } # tell NGINX how to serve PHP files location ~ \.php$ { try_files $uri /index.php =404; fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass unix:/var/run/php/php7.2-fpm.sock; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $request_filename; include fastcgi_params; } # redirect assets with filehash in name to actual filename location ~ ^/dist/ { rewrite "^/dist/css/(.*)\.[0-9a-f]{8}\.css$" /dist/css/$1.css last; rewrite "^/dist/js/(.*)\.[0-9a-f]{8}\.js$" /dist/js/$1.js last; } # serve expires header expires $expires; ssl_certificate /path/to/certificate.pem; # managed by Certbot ssl_certificate_key /path/to/private/key.pem; # managed by Certbot }

Differences

Let's point out some differences that stood out for me:

The NGINX Server Block contains all .htaccess logic but also the logic that was in the Apache Virtual Host. This explains why the server , server_name and root directives (among others) are necessary.

, and directives (among others) are necessary. There are three server directives: 1 that redirect non-https traffic, 1 that redirects traffic to the www-domain, and the last 1 that actually serves the website.

directives: 1 that redirect non-https traffic, 1 that redirects traffic to the www-domain, and the last 1 that actually serves the website. The order of the server and location directives influences how requests are handled. See below for a link that explains how NGINX selects the server and location that will handle a request.

and directives influences how requests are handled. See below for a link that explains how NGINX selects the server and location that will handle a request. The content types for the Expire headers have changed. This might not be due to NGINX, it is worth watching out for this if you are migrating a website.

Learn more about NGINX

One of the strong points of NGINX is that it is very opinionated. This means the docs are very clear about good and bad configurations. Please read the following links carefully if you plan a migration yourself.





Did you think this article was helpful? Can my server block be improved or do you have some additional resources? Let me know in a comment or join the discussion on Reddit!





Other articles