wiki/HowtoNginx.md

560 lines
17 KiB
Markdown
Raw Normal View History

2017-06-21 01:11:54 +02:00
---
categories: web
title: Howto Nginx
...
2016-12-29 11:25:39 +01:00
2017-06-21 01:11:54 +02:00
* Documentation : <https://nginx.org/en/docs/>
2018-02-25 09:31:08 +01:00
* Rôle Ansible : <https://forge.evolix.org/projects/ansible-roles/repository/show/nginx>
2016-12-29 11:25:39 +01:00
2018-02-25 09:31:08 +01:00
[Nginx](https://nginx.org/) est un serveur [HTTP](HowtoHTTP) léger, le deuxième plus utilisé sur le web derrière [Apache](HowtoApache).
2016-12-29 11:25:39 +01:00
## Installation
2018-02-25 09:31:08 +01:00
Nous utilisons les paquets Debian officiels (version 1.10.3 sous Stretch et Jessie-Backports, version 1.6.2 sous Jessie) :
2016-12-29 11:25:39 +01:00
~~~
2018-02-25 09:31:08 +01:00
# apt install nginx-full
2018-02-28 01:13:11 +01:00
$ /usr/sbin/nginx -V
2018-02-25 09:31:08 +01:00
nginx version: nginx/1.10.3
2018-02-28 01:13:11 +01:00
built with OpenSSL 1.1.0f 25 May 2017
TLS SNI support enabled
configure arguments: --with-cc-opt='-g -O2 […]
2018-02-25 09:31:08 +01:00
systemctl status nginx
● nginx.service - A high performance web server and a reverse proxy server
Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
Docs: man:nginx(8)
Process: 6881 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
Process: 6878 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
Main PID: 6882 (nginx)
Tasks: 5 (limit: 4915)
CGroup: /system.slice/nginx.service
├─6882 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
├─6883 nginx: worker process
├─6884 nginx: worker process
├─6885 nginx: worker process
└─6886 nginx: worker process
2016-12-29 11:25:39 +01:00
~~~
2017-07-03 16:47:02 +02:00
2017-06-21 01:11:54 +02:00
## Configuration de base
Fichiers de configuration :
2016-12-29 11:25:39 +01:00
2018-02-25 09:31:08 +01:00
~~~
/etc/nginx/
├── conf.d
├── fastcgi.conf
├── fastcgi_params
├── koi-utf
├── koi-win
├── mime.types
├── modules-available
├── modules-enabled
│ ├── 50-mod-http-auth-pam.conf -> /usr/share/nginx/modules-available/mod-http-auth-pam.conf
│ ├── 50-mod-http-dav-ext.conf -> /usr/share/nginx/modules-available/mod-http-dav-ext.conf
│ ├── 50-mod-http-echo.conf -> /usr/share/nginx/modules-available/mod-http-echo.conf
│ ├── 50-mod-http-geoip.conf -> /usr/share/nginx/modules-available/mod-http-geoip.conf
│ ├── 50-mod-http-image-filter.conf -> /usr/share/nginx/modules-available/mod-http-image-filter.conf
│ ├── 50-mod-http-subs-filter.conf -> /usr/share/nginx/modules-available/mod-http-subs-filter.conf
│ ├── 50-mod-http-upstream-fair.conf -> /usr/share/nginx/modules-available/mod-http-upstream-fair.conf
│ ├── 50-mod-http-xslt-filter.conf -> /usr/share/nginx/modules-available/mod-http-xslt-filter.conf
│ ├── 50-mod-mail.conf -> /usr/share/nginx/modules-available/mod-mail.conf
│ └── 50-mod-stream.conf -> /usr/share/nginx/modules-available/mod-stream.conf
├── 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
2018-02-28 01:13:11 +01:00
~~~
2018-02-25 09:31:08 +01:00
2018-02-28 01:13:11 +01:00
La configuration principale se fait dans le fichier `/etc/nginx/nginx.conf` qui inclut plusieurs fichiers séparés :
2016-12-29 11:25:39 +01:00
~~~
user www-data;
worker_processes auto;
2018-02-28 01:13:11 +01:00
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
2016-12-29 11:25:39 +01:00
events {
2017-06-21 01:11:54 +02:00
use epoll;
worker_connections 10240;
2016-12-29 11:25:39 +01:00
}
http {
2018-02-28 01:13:11 +01:00
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
include /etc/nginx/mime.types;
default_type application/octet-stream;
ssl_prefer_server_ciphers on;
ssl_dhparam /etc/ssl/dhparam.pem;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
gzip on;
gzip_disable "msie6";
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
2016-12-29 11:25:39 +01:00
~~~
2018-02-28 01:13:11 +01:00
Le fichier `/etc/nginx/conf.d/z-evolinux-defaults.conf` contient nos optimisations basiques :
2016-12-29 11:25:39 +01:00
~~~
server_tokens off;
2018-02-28 01:13:11 +01:00
server_names_hash_max_size 512;
server_names_hash_bucket_size 128;
server_name_in_redirect off;
2016-12-29 11:25:39 +01:00
2018-02-28 01:13:11 +01:00
index index.html;
~~~
2016-12-29 11:25:39 +01:00
2018-02-28 01:13:11 +01:00
Le fichier `/etc/nginx/snippets/ipaddr_whitelist` centralise les adresses IP privilégiées, on peut ainsi utiliser `include /etc/nginx/snippets/ipaddr_whitelist;` à différents endroits dans la configuration de Nginx sans dupliquer ces adresses :
2016-12-29 11:25:39 +01:00
~~~
2018-02-28 01:13:11 +01:00
allow 192.0.2.42;
~~~
2016-12-29 11:25:39 +01:00
### Valider la configuration
2018-02-28 01:13:11 +01:00
Avant de redémarrer le serveur, vérifier que vous n'ayez pas introduit des erreurs de syntaxes dans la configuration :
2016-12-29 11:25:39 +01:00
~~~
# nginx -t -c /etc/nginx/nginx.conf
configuration file /etc/nginx/nginx.conf test is successful
~~~
2018-02-28 01:13:11 +01:00
## VirtualHost
De façon similaire à [Apache](HowtoApache), on peut avoir plusieurs **VirtualHost** (sites sur un même serveur HTTP).
Voici un exemple de VirtualHost via `/etc/nginx/sites-available/example` :
~~~
server {
listen 80;
listen [::]:80;
listen 443 ssl;
listen [::]:443 ssl;
ssl_certificate /etc/ssl/certs/www.example.com.chain.pem;
ssl_certificate_key /etc/ssl/private/www.example.com.key;
server_name www.example.com example.com;
root /home/example/www;
location / {
try_files $uri $uri/ =404;
}
location /images/ {
root /home/example/images;
}
location ~ \.css$ {
root /home/example/css;
}
access_log /home/example/log/access.log;
error_log /home/example/log/error.log;
}
~~~
Pour les directives _location_, Nginx va d'abord examiner celles avec des expressions régulières, puis les plus longues
qui seront choisies avant les plus courtes. Dans l'exemple ci-dessous, Nginx vérifiera d'abord si la ressource demandée
se termine par .css puis si elle commence par /images/ puis au final la directive _location/_.
2018-02-28 09:13:12 +01:00
### alias
2016-12-29 11:25:39 +01:00
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 {
2018-02-28 09:13:12 +01:00
alias /foo/bar/fichier.xml;
}
2016-12-29 11:25:39 +01:00
~~~
2018-02-28 09:13:12 +01:00
Quelle différence entre la directive `alias` et `root` ?
2016-12-29 11:25:39 +01:00
2018-02-28 09:13:12 +01:00
`alias` redirige la requête initiale sans le chemin complet tandis que la directive root passe en paramètre le chemin complet.
2016-12-29 11:25:39 +01:00
2017-07-03 16:35:04 +02:00
Exemple :
2016-12-29 11:25:39 +01:00
~~~
2018-02-28 09:13:12 +01:00
location /hello/ {
alias /spool/images/;
2016-12-29 11:25:39 +01:00
}
~~~
2018-02-28 09:13:12 +01:00
Avec `alias`, pour une requête sur `/hello/word.png` Nginx renverra le contenu de `/spool/hello/word.png` tandis qu'avec la directive `root`, elle renverra le contenu de `/spool/images/hello/word.png`.
2016-12-29 11:25:39 +01:00
2018-02-28 09:13:12 +01:00
### proxy_pass
2017-10-09 16:47:23 +02:00
2018-02-28 09:13:12 +01:00
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)
2017-10-09 16:47:23 +02:00
~~~
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;
}
2017-10-09 16:47:23 +02:00
~~~
2018-02-28 09:13:12 +01:00
### rewrite
2016-12-29 11:25:39 +01:00
2018-02-28 09:13:12 +01:00
<http://nginx.org/en/docs/http/ngx_http_rewrite_module.html#rewrite>
2017-01-03 11:20:35 +01:00
2018-02-28 09:13:12 +01:00
On peut faire des ré-écritures basiques (moins puissant que les [Rewrite Rules d'Apache](HowtoApache#rewrite-rules)), par exemple :
~~~
rewrite ^regex$ /vers-cette/uri.exemple.php {last,break,permanent,redirect}
2016-12-29 11:25:39 +01:00
~~~
2018-02-28 09:13:12 +01:00
### real_ip_header
Si les requêtes viennent d'un proxy HTTP (Varnish par exemple), il faut substituer l'adresse IP d'origine par celle contenue dans le header `X-Forwarded-for`.
Cela se fait ainsi en s'assurant d'avoir le paquet `nginx-extras` installé :
~~~
set_real_ip_from 192.0.2.10; # IP du proxy HTTP
2016-12-29 11:25:39 +01:00
real_ip_recursive on;
real_ip_header X-Forwarded-For;
~~~
2018-02-28 09:13:12 +01:00
Cela permet ensuite d'appliquer ensuite des restrictions d'adresses IP par exemple.
2016-12-29 11:25:39 +01:00
### Authentification HTTP
<http://wiki.nginx.org/HttpAuthBasicModule>
Pour configurer une authentification HTTP, on ajoutera les lignes suivantes au sein d'une directive `location` :
2016-12-29 11:25:39 +01:00
~~~
auth_basic "Restricted";
auth_basic_user_file /home/foo/.htpasswd;
2016-12-29 11:25:39 +01:00
~~~
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).
2016-12-29 11:25:39 +01:00
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).
2016-12-29 11:25:39 +01:00
2017-10-17 12:06:06 +02:00
### 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;
[…]
}
~~~
2016-12-29 11:25:39 +01:00
### 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;
2016-12-29 11:25:39 +01:00
}
server {
[…]
2016-12-29 11:25:39 +01:00
# Blacklisting
if ($blacklist) {
rewrite ^ http://donthackmeplz.fr;
}
2016-12-29 11:25:39 +01:00
~~~
### Fermer la connexion sans envoyer de réponse
Dans certains cas, pour protéger le serveur par exemple, on peut être amené à vouloir fermer directement certaines connexions, sans même donner de réponse. Cela peut servir notamment lors d'un DDoS pour bloquer certaines parties d'un site qui peuvent être lourdes (comme une page de recherche) ou certains clients que l'on juge malicieux.
Il suffit alors d'utiliser `return 444`. Exemple :
~~~
location /search {
return 444;
}
~~~
2016-12-29 11:25:39 +01:00
### Cross-domain pour les fonts
~~~
location ~* \.(eot|ttf|woff)$ {
add_header Access-Control-Allow-Origin *;
}
2016-12-29 11:25:39 +01:00
~~~
2018-02-28 09:13:12 +01:00
### http_user_agent
2017-05-26 11:07:55 +02:00
~~~
if ($http_user_agent ~* (DotBot|Cliqzbot|AhrefsBot|SemrushBot)) {
return 404;
}
2017-05-26 11:07:55 +02:00
~~~
2017-07-24 16:32:21 +02:00
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;
}
2017-07-24 16:32:21 +02:00
~~~
2018-02-28 09:13:12 +01:00
### error_page
2016-12-29 11:25:39 +01:00
2018-02-28 09:13:12 +01:00
On peut avoir une page personnalisée selon le code d'erreur HTTP renvoyé.
2017-12-01 22:42:12 +01:00
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 tous les fichiers de confs des vhosts.
2016-12-29 11:25:39 +01:00
Fichier de conf général */etc/nginx/error.conf*
2017-01-03 11:20:35 +01:00
2016-12-29 11:25:39 +01:00
~~~
location /YYYYYY/ {
alias /var/www/;
index page.html;
}
error_page XXX /YYYYYY/;
2017-01-03 11:20:35 +01:00
2016-12-29 11:25:39 +01:00
~~~
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` :
2017-01-03 11:20:35 +01:00
2016-12-29 11:25:39 +01:00
~~~
include /etc/nginx/error.conf;
2017-12-01 22:42:12 +01:00
~~~
2018-01-24 09:26:07 +01:00
2018-02-28 09:13:12 +01:00
### more_set_headers
Si l'on veut manipuler les headers HTTP, on s'assure d'avoir le paquet `nginx-extras` installé, puis on peut utiliser la directive `more_set_headers`. Exemples :
2018-01-25 17:47:11 +01:00
~~~
2018-02-28 09:13:12 +01:00
more_set_headers 'Server: '
more_set_headers 'Server: My Server v42'
2018-01-25 17:47:11 +01:00
~~~
2018-02-28 09:13:12 +01:00
### CGI / FCGI / UWSGI / PHP-FPM
TODO
2018-03-02 10:40:26 +01:00
### Mode maintenance
Il est possible de déclencher très facilement un mode maintenance avec une page spécifique.
En plaçant cette ligne dans votre bloc "location", il suffira que le fichier `maintenance.html` existe pour que Nginx renvoie systématiquement une erreur 503.
~~~
location / {
if (-f /home/example/www/maintenance.html) { return 503; }
[…]
}
~~~
On personnalise alors la page présentée en cas d'erreur 503, en servant cette même page `maintenance.html`
~~~
error_page 503 @maintenance;
location @maintenance {
root /home/example/www;
rewrite ^(.*)$ /maintenance.html break;
}
~~~
En situation normale, on peut avoir le fichier `/home/example/www/_maintenance.html`.
Pour activer le mode maintenance (pas besoin de recharcger Nginx) :
~~~
$ mv /home/example/www/{_,}maintenance.html
~~~
Pour désactiver le mode maintenance (pas besoin de recharcger Nginx) :
~~~
$ mv /home/example/www/{,_}maintenance.html
~~~
2018-02-28 09:13:12 +01:00
## Monitoring
### Munin
Ajouter dans la configuration Nginx :
2018-01-25 17:47:11 +01:00
~~~
2018-02-28 09:13:12 +01:00
location /nginx_status_NNNN {
stub_status on;
allow 127.0.0.1;
allow 192.0.2.42;
deny all;
}
2018-01-25 17:47:11 +01:00
~~~
2018-02-28 09:13:12 +01:00
Puis dans la configuration Munin :
2018-01-25 17:47:11 +01:00
2018-02-28 09:13:12 +01:00
~~~
[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
~~~
## 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](https://www.masv.io/boost-nginx-connection-limits/) 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
voir HowtoSSL (TODO)
~~~
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](HowtoSSL#configuration-nginx)
## 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;
~~~
2018-01-24 09:26:07 +01:00
2018-02-25 09:31:08 +01:00
## FAQ
2018-02-28 09:13:12 +01:00
### Configuration en ligne
<https://nginxconfig.io/>
2018-02-25 09:31:08 +01:00
### Comment prononcer Nginx ?
2018-03-02 10:20:51 +01:00
C'est la première question de la [FAQ officielle](https://www.nginx.com/resources/wiki/community/faq/#how-do-you-pronounce-nginx) du projet. Pour les francophones, voilà notre astuce :
2018-03-02 10:14:54 +01:00
2018-03-02 10:20:51 +01:00
* D'abord le **N** se prononce tout seul, comme dans "haine".
* Puis **gin**, comme dans "imagine", ou "j'ai enfilé mon jean".
* Et enfin **x**, comme dans "j'habite à Aix".
2018-02-28 09:13:12 +01:00
### .htaccess ?
TODO
### Erreurs diverses
2018-02-28 01:13:11 +01:00
<https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/>
2018-02-25 09:31:08 +01:00