Login Logout

Howto Nginx

Nginx est un serveur HTTP léger et puissant.

Installation

# apt install nginx

Sous Debian Stretch, c’est la version 1.10.3 qui est présente dans les dépôts (1.6.2 pour Debian Jessie).

Configuration de base

Fichiers de configuration :

/etc/nginx/
├── conf.d
├── fastcgi.conf
├── fastcgi_params
├── koi-utf
├── koi-win
├── mime.types
├── nginx.conf
├── proxy_params
├── scgi_params
├── sites-available
│   └── default
├── sites-enabled
│   └── default -> /etc/nginx/sites-available/default
├── snippets
│   ├── fastcgi-php.conf
│   └── snakeoil.conf
├── uwsgi_params
└── win-utf

La configuration principale se fait dans le fichier /etc/nginx/nginx.conf :

user www-data;
worker_processes auto;
pid /var/run/nginx.pid;

events {
    use epoll;
    worker_connections 10240;
}

http {
    keepalive_timeout  15;
[…]

L’un des paramètres à ajuster immédiatement est le worker_processes. Les worker processes sont les processus fils lancés par le père (le cœur de nginx), ce sont eux qui font tout le travail, ils ne sont pas multi-threadés : il est recommandé de mettre autant de worker processes que de cores disponibles sur votre serveur ou bien de laisser à « auto ».

Enfin, on peut déléguer un certain nombre d’options générales dans des fichiers /etc/nginx/conf.d/*.conf

# cat /etc/nginx/conf.d/evolix.conf

# disable Nginx version
server_tokens off;

# GZIP
gzip_types text/css application/x-javascript text/javascript;
gzip_comp_level 5;
# http://blog.leetsoft.com/2007/7/25/nginx-gzip-ssl
gzip_buffers 16 8k;

# HTTP cache
expires 12h;

# useful for multi-servers
add_header X-Server $hostname;

Valider la configuration

Avant de redémarrer le serveur, vérifier que vous n’ayez pas introduit des erreurs de syntaxes dans la configuration

# nginx -t -c /etc/nginx/nginx.conf
configuration file /etc/nginx/nginx.conf test is successful

Munin

Ajouter dans la configuration Nginx :

location /nginx_status_NNNN {
    stub_status on;
    access_log   off;
    allow 127.0.0.1;
    allow <IP>;
    deny all;
}

Puis dans la configuration Munin :

[nginx*]
env.url <http://127.0.0.1/nginx_status_NNNN>

On peut ainsi activer les plugins nginx_request et nginx_status :

# cd /etc/munin/plugins
# ln -s /usr/share/munin/plugins/nginx_request nginx_request
# ln -s /usr/share/munin/plugins/nginx_status nginx_status

Rewrite Rule

http://nginx.org/en/docs/http/ngx_http_rewrite_module.html#rewrite

Voici un exemple de ré-écriture :

rewrite ^regex$ /vers-cette/uri.exemple.php {last,break,permanent,redirect}

Ajout d’un alias

Les alias permettent de faire servir du contenu qui ne serait pas stocké dans un répertoire servi par nginx (/var/www/nginx par exemple).

location /crossdomain.xml {
    alias /home/chemin/du/fichier.xml;
}

Différence entre la directive alias et root

L’alias redirige la requête initiale sans le chemin complet tandis que la directive root passe en paramètre le chemin complet.

Exemple :

location /i/ {
    alias /spool/w3/images/;
}

Une requête sur /i/image.png, nginx renverra le contenu de /spool/w3/images/image.png tandis qu’avec la directive root, elle renverra le contenu de /spool/w3/images/i/image.png

Reverse Proxy

Nginx peut aussi agir comme reverse-proxy. On utilisera alors la directive proxy_pass pour définir le serveur vers lequel la requête est envoyée. On peut aussi définir des headers qui seront ajoutés à la requête quand elle est transmise (Notamment, l’IP du visiteur, car le serveur dernière le proxy ne peut voir l’IP de celui-ci)

location / {
    proxy_pass http://localhost:8080;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

Restrictions d’accès derrière un proxy (Varnish par exemple)

Les restrictions d’IPs ne peuvent pas se baser sur le contenu des headers, il faut passer par un module tiers (fourni avec la version nginx-extras) “Real IP”. Il permet de substituer l’adresse IPs d’origine par celle notre choix, et notamment celle contenu dans le header “X-Forwarded-for” :

set_real_ip_from 127.0.0.1; # À remplacer par l'IP du proxy
real_ip_recursive on;
real_ip_header X-Forwarded-For;

Authentification HTTP

http://wiki.nginx.org/HttpAuthBasicModule

Pour configurer une authentification HTTP, on ajoutera les lignes suivantes au sein d’une directive location :

auth_basic            "Restricted";
auth_basic_user_file  /home/foo/.htpasswd;

Attention, si l’on ne souhaite protéger tout le site (location /), il faudra se méfier et bien ajuster la configuration pour s’assurer que tous les fichiers sont protégés (notamment les fichiers PHP par exemple).

Le fichier .htpasswd étant généré avec l’utilitaire htpasswd (comme pour Apache). Bien vérifier les droits en lecture pour l’utilisateur qui fait tourner Nginx (“www-data” en général).

Optimisation

Worker Connections et Keep Alive

Il s’agit du nombre de connexions qu’un worker process peut gérer. Le nombre de connexions multiplié par le nombre de worker processes détermine le nombre total de connexions qu’il est possible de gérer simultanément. Cependant un autre paramètre intervient, il s’agit du keep-alive time-out qui détermine la durée d’une connexion d’un client : lorsqu’un client se connecte au site, la connexion établie n’est pas fermée tout de suite, cela permet de faire passer plusieurs requêtes dans une seule connexion. La connexion sera fermée après le timeout. Il faut donc ajuster ces 2 paramètres en fonction de votre site (astuce : surveiller ses courbes Munin puis ajuster ces paramètres). Une configuration classique est de mettre 10240 en worker_connections et entre 10 et 20 secondes pour keepalive_timeout.

events {
    worker_connections  10240;
}
http {
    keepalive_timeout  15;
[…]

Si l’on souhaite désactiver complètement le Keep Alive, on mettra keepalive_timeout 0;

Diminuer les I/O

Afin d’éviter un goulot d’étranglement sur les I/O, on peut via certains paramètres réduire les accès disques, et donc optimiser l’I/O pour servir le contenu web.

Access Logs

À chaque requête nginx écrit dans le fichier d’access logs. Plus il y a de requêtes plus cela est coûteux en accès disque. Il est donc recommandé d’écrire les access logs dans la mémoire RAM (via une ramdisk par exemple), et de faire un logrotate sur le disque une fois une certaine taille atteinte.

Error Logs

Ne pas oublier de désactiver le mode verbose/debug lorsque le serveur est en production…

Open File Cache

Détermine le nombre de fichiers ouverts à mettre en cache.

Buffers

Si les tampons mémoire ne sont pas assez grands, nginx va devoir écrire dans des tampons temporaires sur le disque dur ce qui augmente les accès disques.

La directive client_body_buffer_size détermine la taille du buffer du champ body d’une requête d’un client. Selon cas il peut être intéressant de l’accorder avec une taille en concordance avec vos formulaires, typiquement si vous avez des gros formulaires avec des données à uploader il faudra augmenter le buffer.

La directive fastcgi_buffers et proxy_buffers sont les buffers associés à php/cgi/… Le concept est le même que le buffer pour les clients. Si les tampons sont trop petits, les données vont être temporairement écrite sur le disque.

Activer la compression gzip

Cette directive permet au visiteur de télécharger des données compressées par le serveur, permettant d’alléger la bande passante. Il est recommandé de mettre gzip_comp_level à 4 ou 5.

On peut aussi servir directement des fichiers déjà compressés : voir http://wiki.nginx.org/HttpGzipStaticModule

Augmenter le nombre maximum de descripteur de fichiers

L’astuce consiste à créer un override systemd de l’unité nginx.

# mkdir -p /lib/systemd/system/nginx.service.d
# cat << EOT > /lib/systemd/system/nginx.service.d/limitnofile
[Service]
LimitNOFILE=16384
EOT
# systemctl daemon-reload
### Mettre la directive worker_rlimit_nofile 16384; dans les premières lignes de nginx.conf
# systemctl restart nginx

SSL

listen 0.0.0.0:443 ssl;
listen [::]:443 ssl;
ssl_certificate /etc/ssl/certs/ssl.example.com.crt;
ssl_certificate_key /etc/ssl/private/ssl.example.com.key;

Attention, ssl_certificate doit contenir toute la chaîne de certification, donc il sera nécessaire de concaténer les certificats. Par exemple, on ajoutera le certificat intermédiaire via :

# cd /tmp
# wget https://www.startssl.com/certs/class1/sha2/pem/sub.class1.server.sha2.ca.pem
# cat sub.class1.server.sha2.ca.pem >> /etc/ssl/certs/ssl.example.com.crt

Pour une configuration plus avancée, voir HowtoSSL

HTTP/2

En version 1.10, la plupart des fonctionnalités du protocole est supportée par Nginx. Note : une version limitée du protocole était déjà supportée depuis Nginx 1.9.5.

Il faut alors activer le protocole dans la directive listen. Sachant que les navigateurs courants ne supportent HTTP/2 que pour des connexions sécurisées, on activera toujours le SSL/TLS. Il est également nécessaire de désactiver le protocole SPDY s’il était utilisé.

listen 0.0.0.0:443 ssl http2;

Divers

Redirection https

server {
    listen 80;
    server_name www.example.com example.com;

    return 301 https://www.example.com$request_uri;
}

server {
    listen 443 ssl;
    server_name example.com;

    return 301 https://www.example.com$request_uri;

    […]
}

server {
    listen 443 ssl;
    server_name www.example.com;

    […]
}

Bloquer selon adresse IP

Le but est de bloquer par adresse IP et de rediriger vers une page « Vous êtes bloqués ».

# Blacklisting
geo $blacklist {
    default 0;
    192.0.2.42/32 1;
}

server {
[…]

    # Blacklisting
    if ($blacklist) {
            rewrite ^ http://donthackmeplz.fr;
    }

Cross-domain pour les fonts

location ~* \.(eot|ttf|woff)$ {
    add_header Access-Control-Allow-Origin *;
}

Selon user-agent

if ($http_user_agent ~* (DotBot|Cliqzbot|AhrefsBot|SemrushBot)) {
    return 404;
}

Si on doit bloquer un user-agent avec des espaces dans son nom, une solution est d’échapper les espaces avec des backslash comme ceci :

if ($http_user_agent ~* (DotBot|Cliqzbot|AhrefsBot|SemrushBot|Go\ 1\.1\ package\ http)) {
   return 404;
}

Page personnalisé selon code erreur HTTP

Contrairement à apache, on ne pourra indiquer une conf général à inclure qui s’appliquera pour tous les vhosts, mais il faudra ajouter l’include sur tout les fichiers de confs des vhosts.

Fichier de conf général /etc/nginx/error.conf

location /YYYYYY/ {
    alias /var/www/;
    index page.html;
}
error_page XXX /YYYYYY/;

Remplacer XXX par le code erreur HTTP souhaité et YYYYY par le nom de Location souhaité (URL) - vu que global à tous les vhosts, prendre une chaîne aléatoire.

Et pour chaque conf des vhosts /etc/nginx/sites-enabled/*.conf :

include /etc/nginx/error.conf;