nginx : un serveur web rapide

Voici une présentation et mini-tutoriel de nginx, un serveur WEB rapide.
Nginx peut servir des pages directement ou être utilisé comme serveur proxy reverse avec mise en cache.
Vous trouverez quelques des explications sur les fichiers de configuration de nginx et notamment des exemples pour WordPress.

Introduction

Initialement, le site était sur varnish comme proxy-reverse et Apache derrière.
Personnellement, j’aime beaucoup varnish, le seul problème est qu’il ne gère pas le SSL.
Ainsi, la partie HTTPs étaient directement délivrés par Apache sans cache ni proxy en amont.

nginx est un serveur WEB qui permet aussi d’être utilisé comme proxy reverse.
Le projet est libre et à débuter en 2002.

Comme pour Apache, Nginx est fournit avec des modules.
Sur la distribution Linux Debian, il existe 3 packages différents plus ou moins légers qui donnent accès ou non à certains modules.

Enfin nginx ne possède pas de module PHP, il est possible d’avoir PHP avec nginx via PHP-FPM.
Les performances de PHP-FPM semblent meilleur que le module Apache.

Fichiers de configuration

L’installation de nginx n’est pas compliqué.
Il fonctionne comme un daemon.

L’arborescence de nginx :

root@www:~# ls -lh /etc/nginx/
total 88K
drwxr-xr-x 2 root root 4,0K déc. 31 12:17 conf.d
-rw-r--r-- 1 root root 1,1K oct. 27 20:22 fastcgi.conf
-rw-r--r-- 1 root root 964 oct. 27 20:22 fastcgi_params
-rw-r--r-- 1 root root 2,8K oct. 27 20:22 koi-utf
-rw-r--r-- 1 root root 2,2K oct. 27 20:22 koi-win
-rw-r--r-- 1 root root 3,9K oct. 27 20:22 mime.types
-rw-r--r-- 1 root root 2,9K janv. 7 21:58 nginx.conf
-rw-r--r-- 1 root root 678 janv. 1 16:57 proxy_params
-rw-r--r-- 1 root root 596 oct. 27 20:22 scgi_params
drwxr-xr-x 2 root root 4,0K janv. 2 20:45 sites-available
drwxr-xr-x 2 root root 4,0K févr. 1 10:40 sites-enabled
drwxr-xr-x 2 root root 4,0K déc. 31 12:15 snippets
-rw-r--r-- 1 root root 623 oct. 27 20:22 uwsgi_params
-rw-r--r-- 1 root root 3,0K oct. 27 20:22 win-utf

Les fichiers importants :

  • /etc/nginx/nginx.conf qui contient la configuration générale de nginx
  • /etc/nginx/proxy_params les paramètres généraux de nginx proxy
  • /etc/nginx/site-enabled et/etc/nginx/site-available qui contient la configuration des sites, le fonctionnement est assez similaire à Apache.

Si on a plusieurs fichiers de configuration, comprenez que l’on pourrait en avoir qu’un seul.
Les paramètres de proxy peuvent être mis dans nginx.conf si vous le souhaitez.
Certains paramètres peuvent être déclarées plusieurs fois et d’autres non.

Parmi le contenu de /etc/nginx/nginx.conf : que vous pouvez modifier pour optimiser (timeout, taille du body, etc).
Pour le KeepAlive, vous pouvez vous rendre sur cette page : https://www.nginx.com/blog/http-keepalives-and-web-performance/

sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 120;
types_hash_max_size 2048;

client_body_timeout 24;
client_header_timeout 24;
client_body_in_file_only on;
client_body_buffer_size 128K;
client_max_body_size 1000M;

Les fichiers de logs par défaut :

  • /var/log/nginx/access.log
  • /var/log/nginx/error.log

Déclaration des sites

Activer un site

Pour créer des sites, il faut que le fichier de configuration soit présent dans /etc/nginx/site-enable
Par défaut, nginx lit tous les fichiers contenus dans ce répertoire, à la fin de /etc/nginx/nginx.conf se trouve ces inclusions :

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

La logique consiste à créer un fichier de configuration dans /etc/nginx/sites-available/ comme/etc/nginx/sites-available/www.exemple.org.conf
pour activer le site, on créé un lien symoblique dans site-enable par la commande :

ln -s /etc/nginx/sites-available/www.exemple.org.conf /etc/nginx/sites-enabled/

on redémarre nginx par : /etc/init.d/nginx reload
Si l’on désire que le fichier de configuration ne soit plus utilisé, il suffit de supprimer/etc/nginx/sites-enabled/www.exemple.org.conf et relancer nginx.
Le fichier de configuration dans sites-available sera toujours présent, si on désire réactivé le site, il suffit de recréer le lien symbolique et relancez nginx.

Déclaration d’un site

Voici un exemple de configuration de déclaration de site sur nginx.
La directive server permet de créer un serveur, vous pouvez créer autant de server que vous le souhaitez dans un fichier de configuration.

La déclaration serveur doit indiquer le port, l’host à écouter.
Dans cette déclaration, on peut donner déclarer des pages pour des actions spécifiques.
Ainsi, il est possible d’avoir sur un même site, la possibilité de servir les pages .html et de renvoyer les pages php vers un autre serveur avec la commande proxy_pass.

Vous trouverez la liste complète des directives sur la page : http://nginx.org/en/docs/http/ngx_http_core_module.html

Vous devez indiquer au moins indiquer le port avec la directive listen.
ainsi que le server_name qui va indiquer l’host à écouter.

root permet d’indiquer le chemin du site, ainsi dans ce cas, nginx n’est pas configuré en proxy mais sert les pages.

Exemple :

server {

listen 80;
server_name www.malekal.com;
access_log /home/logs/apache2/access.log;
error_log /home/logs/apache2/error.log;

root /home/www/www.malekal.com;
...
}

La même en SSL :

server {
 listen 443;
 server_name www.malekal.com;
 ssl on;
 ssl_certificate /etc/ssl/malekal_com.com.crt;
 ssl_certificate_key /etc/ssl/malekal_com.key;
access_log /home/logs/apache2/access.log;
error_log /home/logs/apache2/error.log;

root /home/www/www.malekal.com;

...
}

Il est possible de déclarer plusieurs server_name à la suite.
Il est aussi possible d’utiliser des regex afin de déclarer plusieurs sites comme dans cet exemple.
La directive server_name, créé une variable domain qui est ensuite utilisé pour déclarer le chemin des pages.

server {
 listen 80;
 server_name ~^(www\.)?(?<domain>.+)$;
 access_log /home/logs/apache2/access.log;
 error_log /home/logs/apache2/error.log;

index index.php index.html index.htm;
root /home/www/$domain;

....

}

Déclaration des pages

La déclaration des pages se fait à partir de la direction location.
Location gère le regex, quelques subtilités à connaître.
Syntaxe :

location [ = | ~ | ~* | ^~ ] uri { … }

Par exemple :

location / {}

Va correspondre à toutes les pages, sauf si un autre location peut matcher.

location = / { }

correspond à la racine stricte du site. Les expressions régulières suivantes ne seront pas testées.

  • = : matche l’url exacte
  • ~ : prend en compte la case, les expressions régulières suivantes seront testées.
  • ~* : ne prend pas en compte la case, les expressions régulières suivantes seront testées.
  • ^~ : les expressions régulières suivantes ne seront PAS testées.

ainsi :

location ~ \.php$|/$ {}

prend en compte toutes les pages php.

Ainsi dans l’exemple suivant :

location = / {
   [ configuration A ]
}

location / {
    [ configuration B ]
}

location /documents/ {
    [ configuration C ]
}

location ^~ /images/ {
    [ configuration D ]
}

location ~* \.(gif|jpg|jpeg)$ {
    [ configuration E ]
}
  • Une requête sur / va prendre la configuration A
  • /index.html va prendre la configuration B
  • /documents/document.html va prendre la configuration C
  • /images/1.gif va prendre la configuration D
  • /documents/1.jpg va prendre la configuration E

A partir de chaque location, il est possible de définir une configuration spécifique.

Par exemple, on peut désactiver les logs sur certains types de fichiers :

location ~* ^.+\.(xml|ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|css|rss|atom|js|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
access_log off; log_not_found off; expires max;
}

Par exemple, si l’on veut rediriger l’erreur 403, vers une page spécifique, on peut ajouter :

error_page 403 /403.html;
location = /403.html {
root /home/www/malekal.com;
internal;
}

Nginx permet les includes de fichiers de configuration, ce qui est assez pratique, si vous avez des configurations redondantes (notamment SSL et non SSL).

Nginx en proxy cache

Nginx peut fonctionner en tant que proxy-reverse, le contenu des pages peut alors être mis en cache.
Si le contenu n’est pas présent en cache, la requête sera délivrée au serveur WEB.

Les fichiers de configuration de nginx en proxy :

/etc/nginx/proxy_params :

proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size 10m;
client_body_buffer_size 128k;
client_header_buffer_size 64k;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
proxy_buffer_size 16k;
proxy_buffers 32 16k;
proxy_busy_buffers_size 64k;
send_timeout 600;

Dans le fichier de configuration du site, vous devez déclarer la zone de cache, voici un exemple :

proxy_cache_path  /var/www/cache levels=1:2 keys_zone=http:8m max_size=1000m inactive=600m;
proxy_cache_key "$scheme$host$request_uri";

keys_zone identifie la zone, on l’appelle ici http
Vous devez définir le dossier où sera stocké le caché avec les bonnes permissions

dans la déclaration du serveur, pour utiliser nginx en proxy, on obtient ceci :

server {
 listen 80;
 server_name www.malekal.com;
access_log /home/logs/apache2/access.log;
error_log /home/logs/apache2/error.log;
location / {
proxy_cache http;
add_header X-Proxy-Cache $upstream_cache_status;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://localhost:8080/;
}
}

Dans ce cas précis, les requêtes sont renvoyées vers http://localhost:8080/;
Il faut donc qu’Apache écoute sur ce port, il suffit de modifier le listen depuis le fichier de conf : /etc/apache2/ports.conf
Si vous avez des VirtualHost, pensez à corriger le port sur ces derniers.

Cette configuration modifie le header, afin d’ajouter le X-Forwarded-For
De plus, on ajoute X-Proxy-Cache ce qui permet d’indiquer si la page est cachée ou non.
Si la page est cachée, on obtient cache (HIT), si la page est retournée par le serveur WEB derrière nginx, on obtient (MISS), enfin BYPASS indique qu’aucune configuration cache n’est disponible.

curl -I -s -L uri ou les outils de développement de Chrome ou Firefox permettent de lire le header.

proxy_no_cache permet de définir des exceptions, si des cookies ou arguments sont utilisés :

proxy_no_cache $cookie_nocache $arg_nocache$arg_comment;

Il est aussi possible de prendre en compte le http_cache_control, afin de suivre la politique de cache du navigateur WEB.

proxy_cache_bypass $http_cache_control;

Notez qu’il est tout à fait possible de mélanger nginx en proxy et de délivrer les pages par nginx lui.
Ainsi par exemple, si l’on souhaite que toutes les pages soient livrés par nginx, exceptées les pages en PHP et images qui seront redirigées vers d’autres serveurs WEB (web1 et web2).

proxy_cache_path  /var/www/cache/php levels=1:2 keys_zone=php:8m max_size=1000m inactive=600m;
proxy_cache_path  /var/www/cache/images levels=1:2 keys_zone=images:8m max_size=1000m inactive=600m;
proxy_cache_valid 200 10m;
server {
 listen 80;
 server_name www.malekal.com;
access_log /home/logs/apache2/access.log;
error_log /home/logs/apache2/error.log;

location / {
root /home/www/www.malekal.com;
}

location ~* \.php$ {
proxy_cache php;
add_header X-Proxy-Cache $upstream_cache_status;
proxy_cache_bypass $http_cache_control;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://web1:8080/;
}

location ~* \.(gif|jpg|jpeg|png)$ {
proxy_cache images;
add_header X-Proxy-Cache $upstream_cache_status;
proxy_cache_bypass $http_cache_control;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://web2:8080/;

 }

Si le proxy_pass pointe vers un serveur WEB qui ne répond pas, nginx retourne une erreur 502 bad gateway

Afin d’optimiser le cache, il est possible de placer le cache en mémoire à travers un ramdisk tmpFS (Temporary File System).
Exemple dans fstab, déclaré :

tmpfs  /var/lib/nginx/cache defaults,size=4G  0 0
  • Arrêtez nginx
  • Supprimer le contenu du cache actuel de/var/lib/nginx/cache
  • montez le Ramdisk, avec la commande : mount /var/lib/nginx/cache
  • Relancez nginx qui reconstruira le cache.

NginX et PHP5-FPM

Si vous utilisez Nginx pour délivrer vos pages WEB, sachez qu’il n’existe pas de module PHP comme c’est le cas avec Apache.
Cependant, vous pouvez installer le serveur PHP5-FPM.
Ce dernier fonctionne en daemon et écoute sur son propre port.

Daemon PHP5-FPM

Les fichiers de configuration de PHP5-FPM se trouvent dans /etc/php5/fpm/
Notamment le fichier /etc/php5/fpm/pool.d/www.conf est important.
On y trouve le listen qui indique le port et socket utilisé par fpm

;listen = /var/run/php5-fpm.sock
listen = 127.0.0.1:9000

Il suffit alors de rediriger les requêtes PHP dessus à travers un fastcgi qui fonctionne de la même manière que le proxy_pass.
php5-fpm fonctionne ensuite un peu comme apache, où vous pouvez déclarer le nombre de client max et de spares.

Si le nombre est mal ajusté et insuffisant, dans les logs php5-fpm, vous pouvez obtenir ceci :

[26-Mar-2015 10:10:04] WARNING: [pool www] server reached pm.max_children setting (35), consider raising it
[26-Mar-2015 12:04:52] WARNING: [pool www] seems busy (you may need to increase pm.start_servers, or pm.min/max_spare_servers)

Ainsi, il faut calculer le nombre de client qui peuvent être lancé.
C’est le même principe que pour Apache (se reporter à Optimisation Apache/PHP/MySQL).

Il faut calculer l’occupation moyenne d’un processus php5-fpm et calculer ensuite selon la quantité de mémoire, combien vous pouvez en lancer.
ps –no-headers -o « rss,cmd » -C php5-fpm donne la liste des processus php5-fpm avec la quantité de mémoire utilisée.


La commande suivante permet de calculter la taille moyenne d’un processus php5-fpm

ps --no-headers -o "rss,cmd" -C php5-fpm | awk '{ sum+=$1 } END { printf ("%d%s\n", sum/NR/1024,"Mb") }'

Il ne reste plus qu’à diviser par la taille de mémoire que vous souhaitez allouer à php5-fpm

pm.max_children = 70
pm.start_servers = 20
pm.min_spare_servers = 20
pm.max_spare_servers = 35
pm.max_requests = 500
pm.process_idle_timeout = 10s;

Côté nginx

On déclare la zone fastcgi_cache :

fastcgi_cache_path /var/lib/nginx/php levels=1:2 keys_zone=php:100m inactive=60m;

et dans un bloc php :

location ~ \.php$ {

fastcgi_cache php;
fastcgi_cache_valid 200 60m; # Only cache 200 responses, cache for 60 minutes
add_header X-Fastcgi-Cache $upstream_cache_status;

include fastcgi.conf; # fastcgi_params for nginx < 1.6.1
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_index index.php;

fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;

include /etc/nginx/fastcgi_params;
fastcgi_read_timeout 150;

}

fastcgi_pass donne la redirection vers le serveur php5-fpm.
Ainsi si  fastcgi_pass unix:/var/run/php5-fpm.sock correspond au listen = /var/run/php5-fpm.sock du fichier de configuration /etc/php5/fpm/pool.d/www.conf
Sinon vous pouvez rediriger de cette manière fastcgi_pass 127.0.0.1:9000; si le listen est positionné en listen = 127.0.0.1:9000

Pour interdire le cache, vous pouvez utiliser la directive suivante : fastcgi_cache_bypass 1;
On peut aussi interdire le cache sur certains contenu à travers la directive : fastcgi_no_cache $skip_cache;

Ainsi en amont, on peut par exemple ne pas cacher certains contenus :

if ($request_uri ~* "/wp-admin/|xmlrpc.php|wp-.*.php|/feed/|index.php|sitemap(_index)?.xml") {
 set $skip_cache 1;
 }

Pour tester, si la page est cachée, comme précédemment avec proxy_pass, on peut ajouter une ligne dans le header :

add_header Fastcgi-Cache $upstream_cache_status;

Limit Zone

Le module http_limit_req_module permet de limiter le nombre de requêtes par IP.
Ceci peut s’avérer être une protection contre les attaques DoS.
Plus d’informations sur notre article : Nginx : protection contre les attaques DoS

Voici les grandes lignes du fonctionnement où on déclare en en-tête de fichier la zone, par exemple :

limit_req_zone $binary_remote_addr zone=flood:20m rate=1100r/m;

que l’on peut ensuite appeler comme cela :

server {
...
location ~ \.php$ {
limit_req zone=flood;
...
}
}

Si une IP dépasse le seuil, nginx retourne une erreur 503 (Service Temporarily Unavailable).
Les logs retournent alors :

2017/02/05 19:32:11 [error] 1372#0: *7780519 limiting requests, excess: 0.542 by zone "xxx", client: 91.121.xxxx, server: ~^(www\.)?(?<domain>.+)$, request: "GET / HTTP/1.0", host: "www.malekal.com"

Quelques configurations

WordPress

Voici un exemple de configuration WordPress.

server {
listen 80;
server_name www.malekal.com;
access_log /home/logs/apache2/access.log;
error_log /home/logs/apache2/error.log;

index index.php index.html index.htm;
root /chemin/site;

set $skip_cache 0;

location / {
index index.php index.html index.htm;
try_files $uri $uri/ /index.php?$args;
}

location ~* ^.+\.(xml|ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|css|rss|atom|js|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
access_log off; log_not_found off; expires max;
}

include /etc/nginx/malekal_redirection.conf;

# Les requêtes POST ne vont pas en cache
if ($query_string != "") { set $skip_cache 1; }

# L'administration ne va pas en cache
if ($request_uri ~* "/wp-admin/|xmlrpc.php|wp-.*.php|/feed/|index.php|sitemap(_index)?.xml") { set $skip_cache 1; }

# Les utilisateurs identifiés non plus
if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_no_cache|wordpress_logged_in") { set $skip_cache 1; }

location = /xmlrpc.php { deny all; }

location ~ ^/wp-login.php$ {
include /etc/nginx/allow.conf;
deny all;

fastcgi_pass 127.0.0.1:9000;
include fastcgi.conf;
fastcgi_cache_bypass 1;
fastcgi_no_cache 1;
include fastcgi_params;
}
location ~ \.php$ {
fastcgi_cache_key "$scheme$request_method$host$request_uri";

limit_req zone=flood;
fastcgi_cache php;
fastcgi_cache_valid 200 60m; # Only cache 200 responses, cache for 60 minutes
add_header X-Fastcgi-Cache $upstream_cache_status;

# Regular PHP-FPM stuff:
include fastcgi.conf; # fastcgi_params for nginx < 1.6.1
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;

fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
include /etc/nginx/fastcgi_params;
fastcgi_read_timeout 150;
}

error_page 403 /403.html;
location = /403.html {
root /home/www/malekal.com;
internal;
}
}

Rien de vraiment exceptionnel dans l’exemple.

Il est possible de bloquer l’accès au fichier wp-config.php contre les attaques bruteforce.
Ainsi dans le fichier include /etc/nginx/allow.conf, on déclare les IPs ou masques autorisés.
Exemple :

allow 79.143.141.0/24;
 allow 93.20.128.0/19;
 allow 213.215.7.75;

Le fichier /etc/nginx/malekal_redirection.conf contient des rewrites qui permettent de rediriger d’anciennes URLs

rewrite ^/tutorial_USBFix.php$ https://www.malekal.com/tutoriel-usbfix/ permanent;
rewrite ^/securiser_Firefox.php$ https://www.malekal.com/securiser-le-navigateur-web-firefox-2/ permanent;
rewrite ^/ordinateur_lent.php$ https://www.malekal.com/comprendre-pourquoi-votre-ordinateur-est-ralenti-2/ permanent;

Rewrite

nginx supporte le rewrite, la syntaxe est assez similaire à celle d’Apache.
Voici quelques exemples :

 rewrite ^/post([0-9]+)\.html$ /viewtopic.php?p=$1 break;
 rewrite ^/sujets-actifs(-([0-9]+))?\.html$ /search.php?search_id=active_topics&start=$2&sr=topics break;
 rewrite ^/sans-reponses(-([0-9]+))?\.html$ /search.php?search_id=unanswered&start=$2&sr=topics break;
 rewrite ^/non-lu(-([0-9]+))?\.html$ /search.php?search_id=unreadposts&start=$2 break;
 rewrite ^/equipe\.html$ /memberlist.php?mode=leaders break;
 rewrite ^/(forum|[a-z0-9_-]*-f)([0-9]+)(-([0-9]+))?\.html$ /viewforum.php?f=$2&start=$4 break;
 rewrite ^/(forum|[a-z0-9_-]*-f)([0-9]+)/(topic|[a-z0-9_-]*-t)([0-9]+)(-([0-9]+))?\.html$ /viewtopic.php?f=$2&t=$4&start=$6 break;
 rewrite ^/([a-z0-9_-]*)/?(topic|[a-z0-9_-]*-t)([0-9]+)(-([0-9]+))?\.html$ /viewtopic.php?forum_uri=$1&t=$3&start=$5 break;
 rewrite ^/(groupe|[a-z0-9_-]*-g)([0-9]+)(-([0-9]+))?\.html$ /memberlist.php?mode=group&g=$2&start=$4 break;
 rewrite ^/membre/([^/]+)/(topics|posts)/?(page([0-9]+)\.html)?$ /search.php?author=$1&sr=$2&start=$4 break;
 rewrite ^/nouveaux-messages(-([0-9]+))?\.html$ /search.php?search_id=newposts&start=$2&sr=topics break;

Liens

image_pdfimage_print
(Visité 2 864 fois, 1 visites ce jour)