Skip to content

About Nginx

Nginx is an HTTP and reverse proxy server, a mail proxy server, and a generic TCP/UDP proxy server. (Pronunced engine-x)

Installation of HTTP server

In order to display web pages to our site visitors, we are going to employ Nginx, a modern, efficient web server.

All of the software we will be using for this procedure will come directly from Debian's default package repositories. This means we can use the apt package management suite to complete the installation.

Since this is our first time using apt for this session, we should start off by updating our local package index. We can then install the server.

apt update && apt full-upgrade -y
apt install nginx -y

Now, test if the server is up and running by accessing your server's domain name or public IP address in your web browser. If you do not have a domain name pointed at your server and you do not know your server's public IP address, you can find it by typing one of the following into your terminal.

ip addr show YOUR_INTERFACE_NAME | grep inet | awk '{ print $2; }' | sed 's/\/.*$//'

Installing PHP support

Since Nginx does not contain native PHP processing like some other web servers, we will need to install fpm, which stands for “fastCGI process manager”. We will tell Nginx to pass PHP requests to this software for processing. We'll also install an additional helper package that will allow PHP to communicate with our MySQL database backend. The installation will pull in the necessary PHP core files to make that work.

apt install php-fpm php-mysql

Configuring nginx to work with php-fpm

We create a new directory in /var/www to keep our php websites.

sudo mkdir /var/www/your.website.com

Add configuration at nginx by editing

vim /etc/nginx/sites-available/your.website.com.conf

You can edit /etc/nginx/sites-available/your.website.com.conf and add

server {
    listen 80;
    listen [::]:80;

    root /var/www/your.website.com;
    index index.php index.html index.htm;

    server_name your.website.com;

    location / {
        try_files $uri $uri/ =404;
    }

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
    }
}

Now we activate our configuration in nginx and we test if it works !

ln -s /etc/nginx/sites-available/your.website.com.conf /etc/nginx/sites-enabled/
nginx -t # testing conf files synthax

If the test is successfull you can reload nginx config

systemctl reload nginx.service

Test PHP support

Create a new php file called info.php in /var/www/your.website.com

touch /var/www/your.website.com/info.php

Edit this file and add

<?php
  phpinfo();
?>

Reach your website at http://your.website.com/info.php to verify if the page display information about your php installation.

You can also reach your webiste with curl -I your.website.com/info.php who should display

curl_website

Adjust timezone

Sometimes during the installation the timezone isn't set up you will must fix by yourself.

Edit /etc/php/7.0/fpm/php.ini

Find date.timezone = UTC value and modify like that date.timezone = Europe/Brussels

Security tweaks

Warning

This section cover the installation of SSL/TLS Certificate if you will go far with php conf (install more modules as example) you can go on official website of nginx.

On this section we will put all fine-tunings to improve security level on your nginx web server but TAKE CARE and Keep in mind that security settings can also affect your installation. As some security mechanisms aren't supported by older devices & browsers.

Generation of dh file & add feature to nginx

sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 4096
sudo chmod 600 /etc/ssl/certs/dhparam.pem
mv /etc/ssl/certs/dhparam.pem /etc/nginx/dhparam.pem

Add ssl_dhparam /etc/nginx/dhparam.pem; at your nginx.conf and restart the service systemctl restart nginx.service

Example of secure nginx

Public example

# read more here http://tautt.com/best-nginx-configuration-for-security/

# don't send the nginx version number in error pages and Server header
server_tokens off;

# config to don't allow the browser to render the page inside an frame or iframe
# and avoid clickjacking http://en.wikipedia.org/wiki/Clickjacking
# if you need to allow [i]frames, you can use SAMEORIGIN or even set an uri with ALLOW-FROM uri
# https://developer.mozilla.org/en-US/docs/HTTP/X-Frame-Options
add_header X-Frame-Options SAMEORIGIN;

# when serving user-supplied content, include a X-Content-Type-Options: nosniff header along with the Content-Type: header,
# to disable content-type sniffing on some browsers.
# https://www.owasp.org/index.php/List_of_useful_HTTP_headers
# currently suppoorted in IE > 8 http://blogs.msdn.com/b/ie/archive/2008/09/02/ie8-security-part-vi-beta-2-update.aspx
# http://msdn.microsoft.com/en-us/library/ie/gg622941(v=vs.85).aspx
# 'soon' on Firefox https://bugzilla.mozilla.org/show_bug.cgi?id=471020
add_header X-Content-Type-Options nosniff;

# This header enables the Cross-site scripting (XSS) filter built into most recent web browsers.
# It's usually enabled by default anyway, so the role of this header is to re-enable the filter for
# this particular website if it was disabled by the user.
# https://www.owasp.org/index.php/List_of_useful_HTTP_headers
add_header X-XSS-Protection "1; mode=block";

# with Content Security Policy (CSP) enabled(and a browser that supports it(http://caniuse.com/#feat=contentsecuritypolicy),
# you can tell the browser that it can only download content from the domains you explicitly allow
# http://www.html5rocks.com/en/tutorials/security/content-security-policy/
# https://www.owasp.org/index.php/Content_Security_Policy
# I need to change our application code so we can increase security by disabling 'unsafe-inline' 'unsafe-eval'
# directives for css and js(if you have inline css or js, you will need to keep it too).
# more: http://www.html5rocks.com/en/tutorials/security/content-security-policy/#inline-code-considered-harmful
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://ssl.google-analytics.com https://assets.zendesk.com https://connect.facebook.net; img-src 'self' https://ssl.google-analytics.com https://s-static.ak.facebook.com https://assets.zendesk.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://assets.zendesk.com; font-src 'self' https://themes.googleusercontent.com; frame-src https://assets.zendesk.com https://www.facebook.com https://s-static.ak.facebook.com https://tautt.zendesk.com; object-src 'none'";

# redirect all http traffic to https
server {
  listen 80 default_server; #only if its your first server in an other case remove default_server from the line
  listen [::]:80 default_server;
  server_name .forgott.com;
  return 301 https://$server_name$request_uri;
}

server {
  listen 443 ssl http2;
  listen [::]:443 ssl http2;
  server_name your.website.com;

  ssl_certificate /etc/nginx/ssl/star_forgott_com.crt;
  ssl_certificate_key /etc/nginx/ssl/star_forgott_com.key;
  root /var/www/your.website.com;
  # enable session resumption to improve https performance
  # http://vincent.bernat.im/en/blog/2011-ssl-session-reuse-rfc5077.html
  ssl_session_cache shared:SSL:50m;
  ssl_session_timeout 1d;
  ssl_session_tickets off;

  # Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits
  ssl_dhparam /etc/nginx/ssl/dhparam.pem;

  # enables server-side protection from BEAST attacks
  # http://blog.ivanristic.com/2013/09/is-beast-still-a-threat.html
  ssl_prefer_server_ciphers on;
  # disable SSLv3(enabled by default since nginx 0.8.19) since it's less secure then TLS http://en.wikipedia.org/wiki/Secure_Sockets_Layer#SSL_3.0
  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
  # ciphers chosen for forward secrecy and compatibility
  # http://blog.ivanristic.com/2013/08/configuring-apache-nginx-and-openssl-for-forward-secrecy.html
  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';

  # enable ocsp stapling (mechanism by which a site can convey certificate revocation information to visitors in a privacy-preserving, scalable manner)
  # http://blog.mozilla.org/security/2013/07/29/ocsp-stapling-in-firefox/
  resolver 8.8.8.8 8.8.4.4;
  ssl_stapling on;
  ssl_stapling_verify on;
  ssl_trusted_certificate /etc/nginx/ssl/star_forgott_com.crt;

  # config to enable HSTS(HTTP Strict Transport Security) https://developer.mozilla.org/en-US/docs/Security/HTTP_Strict_Transport_Security
  # to avoid ssl stripping https://en.wikipedia.org/wiki/SSL_stripping#SSL_stripping
  # also https://hstspreload.org/
  add_header Strict-Transport-Security "max-age=31536000; includeSubdomains; preload";

  # ... the rest of your configuration
  location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
    }
    location / {
        try_files $uri $uri/ =404;
    }
}

Our SSL Certificate A+ approved with full compliance

# HTTP Server
server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name your.domain.com;

    # Redirect all HTTP requests to HTTPS with a 301 Moved Permanently response.
    return 301 https://$server_name$request_uri;
}

# HTTPS Server
server {
    listen 443 ssl http2;
    server_name your.domain.com;

    error_log /var/log/nginx/your_service.log;

    root /var/www/your.website.com;
    ssl on;
    ssl_certificate /etc/letsencrypt/live/your.domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/your.domain.com/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/your.domain.com/chain.pem;
    ssl_protocols TLSv1.2;
    ssl_prefer_server_ciphers on;
    ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
    ssl_dhparam /etc/nginx/dhparam.pem;
    ssl_session_timeout 10m;
    ssl_session_cache shared:SSL:10m;
    ssl_session_tickets off;
    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 1.1.1.1 1.0.0.1;
    resolver_timeout 5s;
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
    add_header X-Frame-Options DENY;
    add_header X-Content-Type-Options nosniff;
    add_header X-XSS-Protection "1; mode=block";

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
    }
    location / {
        try_files $uri $uri/ =404;
    }

}

Example with proxy support

# Upstreams
upstream myproxy {
    server 127.0.0.1:3000;
}
# HTTP Server
server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name your.domain.com;

    # Redirect all HTTP requests to HTTPS with a 301 Moved Permanently response.
    return 301 https://$server_name$request_uri;
}

# HTTPS Server
server {
    listen 443 ssl http2;
    server_name your.domain.com;

    error_log /var/log/nginx/your_service.log;

    ssl on;
    ssl_certificate /etc/letsencrypt/live/your.domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/your.domain.com/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/your.domain.com/chain.pem;
    ssl_protocols TLSv1.2;
    ssl_prefer_server_ciphers on;
    ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
    ssl_dhparam /etc/nginx/dhparam.pem;
    ssl_session_timeout 10m;
    ssl_session_cache shared:SSL:10m;
    ssl_session_tickets off;
    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 1.1.1.1 1.0.0.1;
    resolver_timeout 5s;
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
    add_header X-Frame-Options DENY;
    add_header X-Content-Type-Options nosniff;
    add_header X-XSS-Protection "1; mode=block";

    location / {
        proxy_pass http://myproxy/; #defined by upstream name
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $http_host;

        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forward-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forward-Proto http;
        proxy_set_header X-Nginx-Proxy true;

        proxy_redirect off;
    }
    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
    }
}

Forward Secrecy & Diffie Hellman Ephemeral parameters

The concept of forward secrecy is simple: client and server negotiate a key that never hits the wire, and is destroyed at the end of the session. The RSA private from the server is used to sign a Diffie-Hellman key exchange between the client and the server. The pre-master key obtained from the Diffie-Hellman handshake is then used for encryption. Since the pre-master key is specific to a connection between a client and a server, and used only for a limited amount of time, it is called Ephemeral.

With Forward Secrecy, if an attacker gets a hold of the server's private key, it will not be able to decrypt past communications. The private key is only used to sign the DH handshake, which does not reveal the pre-master key. Diffie-Hellman ensures that the pre-master keys never leave the client and the server, and cannot be intercepted by a MITM.

All versions of nginx as of 1.4.4 rely on OpenSSL for input parameters to Diffie-Hellman (DH). Unfortunately, this means that Ephemeral Diffie-Hellman (DHE) will use OpenSSL's defaults, which include a 1024-bit key for the key-exchange. Since we're using a 2048-bit certificate, DHE clients will use a weaker key-exchange than non-ephemeral DH clients.

Source : Raymii Linux SysAdm

TLS status verification

You can use several tools to get informations about your domain and his certificate.

As example with nmap

nmap --script ssl-enum-ciphers your.domain.com -p 443

With openssl

openssl s_client -connect your.domain.com:443 -servername your.domain.com

Bug Fix

OpCache not working

If your values entered on /etc/php/7.0/fpm/php.ini for opcache not working you can fix with editing /etc/php/7.0/mods-available/opcache.ini.

; configuration for php opcache module
; priority=10
zend_extension=opcache.so
opcache.enable=1
opcache.enable_cli=1
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=10000
opcache.memory_consumption=128
opcache.save_comments=1
opcache.revalidate_freq=1

And reload php-fpm by typing systemctl restart php-7.0-fpm.service

"Server certificate is incomplete" error

If SSL Lab Test return error "Server certificate is incomplete" you must check if you have well set up your certificates.

ssl_certificate           /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key       /etc/letsencrypt/live/yourdomain.com/privkey.pem;
ssl_trusted_certificate   /etc/letsencrypt/live/yourdomain.com/chain.pem;