Setting up Nginx with PHP and Let’s Encrypt in Ubuntu Linux – a beginner-friendly guide

Foreword

I decided to write a complete guide myself on how to set up an Nginx web server on Linux, with Let’s Encrypt TLS (SSL) and PHP enabled. Nginx is a lightweight but full-featured webserver that can perform much better than Apache and other alternatives. This means you get more performance with less resources. If you don’t need the advanced features that Apache can give you, Nginx is a splendid choice!

– This guide talks about installing Nginx, PHP and a Let’s Encrypt SSL certificate
– It mentions the configuration of TLS (SSL) connections
– It helps you setting up your first Nginx server block (virtual host)

This article will cover the latest Ubuntu 16.04.1 as of writing and the Nginx 1.11.9 community edition. This version of Nginx is not available in the main Ubuntu repository, as it’s part of the Mainline (read: development) release cycle and not the Stable one. You can go ahead and install the stable 1.10 version if you prefer the officially labelled stable version, but I decided to use the 1.11 version because I’d want to make use of the new ssl_ecdh_curve feature, which is useful for our TLS configuration, that allows you to specify a list of curves, as described in their changelog at http://nginx.org/en/CHANGES:

Feature: the "ssl_ecdh_curve" directive now allows specifying a list
of curves when using OpenSSL 1.0.2 or newer; by default a list built
into OpenSSL is used.

This article assumes that you already have an installation of Ubuntu that is ready for use. For the rest, I’ll try to write the guide as beginner-friendly as possible.

Installing and configuring the latest Nginx

To be able to install the latest Nginx 1.11.9 version, you need to add the official repository to your APT sources.list. So open up a terminal window!

sudo nano /etc/apt/sources.list

Keep pressing the Page Down button to go to the bottom of the file and add these lines. I’ve commented out the deb-src line because we don’t need to source code.

# Nginx
deb http://nginx.org/packages/mainline/ubuntu/ xenial nginx
#deb-src http://nginx.org/packages/mainline/ubuntu/ xenial nginx

Just for a reference, if you prefer to use the Stable labelled version, you’d prefer to replace the above lines with these:

deb http://nginx.org/packages/ubuntu/ xenial nginx
# deb-src http://nginx.org/packages/ubuntu/ xenial nginx

This way, you’ll always have the latest stable version Nginx that is available for your Ubuntu Xenial Xerus version. Press CTRL + X to close the file, choose Y and press Return to overwrite the file.

Update the APT software list by running:

sudo apt update

Now that the list is updated and the latest Nginx version is available in APT, install Nginx, PHP and the Let’s Encrypt tool:

sudo apt install nginx php letsencrypt

At this point you have the Nginx web server installed. Now let’s do some basic configuration. In this guide I’m only focussing on a basic working setup of Nginx and no fine-tuning, so no Gzip, caching, etc.

This is the nginx.conf file in /etc/nginx

user  www-data;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
  worker_connections  1024;
}

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

  log_format  main  '$remote_addr - $remote_user [$time_local] "$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;

  keepalive_timeout  65;

  #gzip  on;

  include /etc/nginx/sites-enabled/*.conf;
}

First navigate to the /etc/nginx directory and stay in that directory for now.

cd /etc/nginx

Create this file with the following command (if it already exists, it will just be opened for editing in the terminal), also remember it for next files we’ll create.

sudo nano nginx.conf

Exit and save using the keys mentioned before.

Note that using the sites-enabled subfolder makes it compatible with external tools to manipulate Nginx configuration, like Webmin. The point is to have a sites-available subfolder for all your server block (in Apache language: virtual host) configurations. As soon as you’re ready to make that server block online, you copy – or symlink to – the file in the sites-enabled subfolder in /etc/nginx.

Create the user and group www-data. Nginx will be running as this user

sudo addgroup www-data
sudo adduser www-data www-data

Create the necessary subfolders if they don’t already exist:

sudo mkdir conf.d
sudo mkdir sites-available
sudo mkdir sites-enabled

I use conf.d as a folder for global configurations that are used by multiple server blocks. In that folder create the http.conf (or whatever name you want to use for it) file. These are the contents to enable PHP support, Let’s Encrypt automatic certification file creation and some smaller things.

# PHP configuration
location ~ \.php$ {
    fastcgi_pass unix:/run/php/php7.0-fpm.sock;
    include /etc/nginx/fastcgi_params;
    fastcgi_intercept_errors on;
}

# Deny all attempts to access hidden files such as .htaccess, .htpasswd, etc.
location ~ /\. {
    deny  all;
}

# Avoid error and access logging of requests to favicon.ico files
# Quite annoying so see everywhere in your logs if you don't have one
location = /favicon.ico {
    log_not_found off;
    access_log off;
}

# Avoid error and access logging of requests to robots.txt files
# Uncomment the lines below to use it
#location = /robots.txt {
#    allow all;
#    log_not_found off;
#    access_log off;
#}

# Allow access to the folder by Let's Encrypt
# It's automatically generated by LE
location ^~ /.well-known/ {
   default_type "text/plain";
   allow all;
}

Next up is the mime.types, create it in /etc/nginx. This file is there to give Nginx knowledge on which mime type to pass with the files containing these extensions, so that the browser quickly knows what to do with it. Otherwise the browser would standard offer the file as a download or display it as plain text, depending on its own settings.

types {
    text/html html htm shtml;
    text/css css;
    text/xml xml;
    image/gif gif;
    image/jpeg jpeg jpg;
    application/javascript js;
    application/atom+xml atom;
    application/rss+xml rss;
    text/mathml mml;
    text/plain txt;
    text/vnd.sun.j2me.app-descriptor jad;
    text/vnd.wap.wml wml;
    text/x-component htc;
    image/png png;
    image/tiff tif tiff;
    image/vnd.wap.wbmp wbmp;
    image/x-icon ico;
    image/x-jng jng;
    image/x-ms-bmp bmp;
    image/svg+xml svg svgz;
    image/webp webp;
    application/font-woff woff;
    application/java-archive jar war ear;
    application/json json;
    application/mac-binhex40 hqx;
    application/msword doc;
    application/pdf pdf;
    application/postscript ps eps ai;
    application/rtf rtf;
    application/vnd.apple.mpegurl m3u8;
    application/vnd.ms-excel xls;
    application/vnd.ms-fontobject eot;
    application/vnd.ms-powerpoint ppt;
    application/vnd.wap.wmlc wmlc;
    application/vnd.google-earth.kml+xml kml;
    application/vnd.google-earth.kmz kmz;
    application/x-7z-compressed 7z;
    application/x-cocoa cco;
    application/x-java-archive-diff jardiff;
    application/x-java-jnlp-file jnlp;
    application/x-makeself run;
    application/x-perl pl pm;
    application/x-pilot prc pdb;
    application/x-rar-compressed rar;
    application/x-redhat-package-manager rpm;
    application/x-sea sea;
    application/x-shockwave-flash swf;
    application/x-stuffit sit;
    application/x-tcl tcl tk;
    application/x-x509-ca-cert der pem crt;
    application/x-xpinstall xpi;
    application/xhtml+xml xhtml;
    application/xspf+xml xspf;
    application/zip zip;
    application/octet-stream bin exe dll;
    application/octet-stream deb;
    application/octet-stream dmg;
    application/octet-stream iso img;
    application/octet-stream msi msp msm;
    application/vnd.openxmlformats-officedocument.wordprocessingml.document docx;
    application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx;
    application/vnd.openxmlformats-officedocument.presentationml.presentation pptx;
    audio/midi mid midi kar;
    audio/mpeg mp3;
    audio/ogg ogg;
    audio/x-m4a m4a;
    audio/x-realaudio ra;
    video/3gpp 3gpp 3gp;
    video/mp2t ts;
    video/mp4 mp4;
    video/mpeg mpeg mpg;
    video/quicktime mov;
    video/webm webm;
    video/x-flv flv;
    video/x-m4v m4v;
    video/x-mng mng;
    video/x-ms-asf asx asf;
    video/x-ms-wmv wmv;
    video/x-msvideo avi;
}

Not done yet! One more file to add in that folder, fastcgi_params. This configuration tells the web server what data to pass with CGI requests.

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  REQUEST_SCHEME     $scheme;
fastcgi_param  HTTPS              $https if_not_empty;

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;

fastcgi_index  index.php;

# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param  REDIRECT_STATUS    200;

These files were already added in the previously made configuration files, so no extra modification is needed.

TLS configuration for Nginx

While it is still common to call this SSL, I’d rather call it with it’s real name, because we’re not going to add support for the old and broken SSL technology, just the new and safe TLS. We’re still going to add support for TLS 1.0 just for means of compatibility with older browsers. We’re going to set it up so we have a good rating at ssllabs.com, which means our HTTPS connections are very safe and secure, just the way they are meant to be. The certifications we’ll get for free using the Let’s Encrypt service, but you’ll need to have set up your domain name already and pointed it to your server’s IP address. If you don’t have one already, you’ll have to take care of that first.

We can start by adding the ssl.conf file in /etc/nginx/conf.d.

# TLS configuration according to Mozilla's Intermediate compatibility recommendation

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers "ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS";
ssl_ecdh_curve secp384r1:prime256v1:secp521r1;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
ssl_session_timeout 1d;

ssl_dhparam /etc/nginx/dhparam.pem;

# OCSP Stapling ---
# fetch OCSP records from URL in ssl_certificate and cache them
ssl_stapling on;
ssl_stapling_verify on;

# DNSCrypt.eu public DNS resolvers
resolver 176.56.237.171 77.66.84.233;
# valid overwrites the TTL value of a DNS answer
#resolver 176.56.237.171 77.66.84.233 valid=300s;
resolver_timeout 5s;

#HSTS - in production increase max age to second one
add_header Strict-Transport-Security max-age=300;
#add_header Strict-Transport-Security max-age=15768000;

add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;

We need to generate a stronger DHE parameter, because the default 1024 bits one in OpenSSL isn’t good enough for our 2048 bits Let’s Encrypt certificate, as it decreases security. Be aware that the following command can take quiet a while depending on the power of the computer that you’re running the command on.

cd /etc/nginx/dhparam.pem
openssl dhparam -out dhparam.pem 2048

Create the webroot folder of your website and Let’s Encrypt SSL certificate

mkdir /var/www/domain.com
sudo letsencrypt certonly --webroot -w /var/www/domain.com -d domain.com -d www.domain.com

If everything went well, a folder /etc/letsencrypt has been created with a subfolder live/ where the certificates have been put into another folder with your domain name as name. So that should be /etc/letsencrypt/live/domain.com. Next up, we’re going to create our first server block.

Creating server blocks (virtual hosts) in Nginx

The following configuration file will enable the webserver to listen at port 80 and 443 for HTTPS connections on both HTTP and HTTP2 protocols, the latter being a new and optimized version. We’re adding the SSL certificates generated by the Let’s Encrypt tool, which is the last step to enable trustworthy HTTPS connections. Create the file in /etc/nginx/sites-available and as soon as you’re ready to put it live, copy it to the sites-enabled folder.

server {
    listen 80;
    listen 443 ssl http2;

    server_name  domain.com www.domain.com;

    ssl_certificate /etc/letsencrypt/live/domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/domain.com/privkey.pem;

    #charset koi8-r;
    access_log  /var/log/nginx/domain.com.access.log  main;
    error_log  /var/log/nginx/domain.com.error.log warn;

     root   /var/www/domain.com/public_html;
     index index.php index.html index.htm;

    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

    # proxy the PHP scripts to Apache listening on 127.0.0.1:80
    #
    #location ~ \.php$ {
    #    proxy_pass   http://127.0.0.1;
    #}

    # This is important to include all the global configurations. Has to be added to every server block
    include /etc/nginx/conf.d/*.conf;
}

Now restart Nginx:

sudo service nginx restart

Last words

There you go, a full installation of Nginx with PHP and TLS (SSL) encryption.

If you have any questions, post them below. If you want to tell me your opinion about this guide, which is also my first blog post, let me know!

Sources:

https://wiki.mozilla.org/Security/Server_Side_TLS#Intermediate_compatibility_.28default.29

https://cipherli.st

https://angristan.fr/configurer-https-nginx

https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html#Forward_Secrecy_&_Diffie_Hellman_Ephemeral_Parameters

https://certbot.eff.org/#ubuntuxenial-nginx

Leave a Reply

Your email address will not be published. Required fields are marked *