Ma configuration Nginx

Les pages de ce site sont désormais servies par Nginx depuis quelques semaines, et j’en suis totalement ravi. Au début il y a eu une période d’incertitude et de petite frayeur du au fait de basculer d’Apache que je maitrise relativement bien, vers une plateforme toute nouvelle avec d’autres habitudes à prendre, d’autres conventions, un style différent (mais pas tant que ça). D’ailleurs en parlant de petite frayeur, avant-hier les sites hébergés sur le Kimsufi ont été indisponibles durant environ 4 heures, j’ai bataillé et sué fort pour trouver mon erreur, ma foi comme souvent, un peu bête (*).

Ce billet fait un peu le point sur ma configuration actuelle de Nginx. J’ai essayé de la faire le plus modulaire possible en créant des blocs relatifs à certains environnements qui sont ou non inclus dans les server blocks (le nom des virtual hosts chez Nginx). Par exemple, j’ai un morceau de code pour les spécificités de WordPress, de Koken ou pour ajouter la gestion HTTPS (SSL).

Configuration

Avec Fedora, la configuration de Nginx se situe sous /etc/nginx, j’y ai créé 2 autres répertoires /etc/nginx/global et /etc/nginx/vhosts. Dans le premier je place tous les blocs de code possiblement redondants – ceux cités plus haut – et dans le second sont placés tous les server blocks, personnellement je crée un fichier par domaine, par exemple j’ai /etc/nginx/vhosts/feub.net.conf pour feub.net.
Le fichier lu par Nginx est /etc/nginx/nginx.conf, je ne l’ai pour ainsi dire pas touché, j’ai juste ajouté un include vers mon répertoire de vhosts, prenant en compte les fichiers se terminant par .conf uniquement :

include /etc/nginx/vhosts/*.conf;

Et voici un exemple de fichier de configuration pour le domaine feub.net :

server {
	listen          80;
	server_name     www.feub.net;
	
	rewrite ^(.*) 	http://feub.net$1 permanent;
}

server {
	listen          80;
	server_name     feub.net;

	root            /var/www/websites_root/feub.net/www/html;
	index           index.php index.html index.htm;

	access_log      /var/log/nginx/feub.net.access.log;
	error_log       /var/log/nginx/feub.net.error.log;

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

	include global/php.conf;
	include global/wordpress.conf;
	include global/things_not_logged.conf;
}

J’aime bien forcer l’accès au site sans le www (c’est très 2000s les sites en 3W ;P), donc le premier bloc écoute ce qui arrive sur www.feub.net et redirige (redirection permanete 301) vers la version sans www.
On voit qu’en fin de fichier j’inclus des morceaux spécifiques, dans l’ordre : prise en charge des fichiers PHP, configuration de base relative à WordPress et mon fichier things_not_logged.conf regroupant des choses comme ne pas logger robots.txt, ignorer les .htaccess, etc. Ci-dessous le détail de ces fichiers.

location ~ \.php$ {
	include         fastcgi_params;
	fastcgi_intercept_errors on;
	fastcgi_read_timeout    240s;
	fastcgi_pass    unix:/var/run/php-fpm.sock;
	fastcgi_index   index.php;
	fastcgi_param   SCRIPT_FILENAME  $document_root$fastcgi_script_name;
}
# Interdit l'accès aux zones non publiques
# ou limitation de l'accès en interne seulement
location ~* wp-admin/includes { deny all; }
location ~* wp-includes/theme-compat/ { deny all; }
location ~* wp-includes/js/tinymce/langs/.*\.php { deny all; }
location /wp-content/ { internal; }
location /wp-includes/ { internal; }
location ~* wp-config.php { deny all; }
location /medias/ { internal; }

# Empêche l'exécution de certains fichiers dans le répertoire d'upload
# en forçant leur type MIME à text/plain
location ~* ^/wp-content/uploads/.*.(html|htm|shtml|php|js|swf)$ {
        types { }
        default_type text/plain;
}

# Ajoute un slash final aux requêtes vers */wp-admin 
rewrite /wp-admin$ $scheme://$host$uri/ permanent;
# Ne pas logger les requêtes vers favicon.ico
location = /favicon.ico {
        log_not_found off;
        access_log off;
}

# Ne pas logger les requêtes vers robots.txt
location = /robots.txt {
        allow all;
        log_not_found off;
        access_log off;
}

# Maximise la durée de vie des fichiers statiques
location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
        expires max;
        log_not_found off;
}
# Active la compression gzip pour de meilleures peformances
gzip on;
gzip_comp_level 6;
gzip_types text/css text/javascript application/json application/javascript application/x-javascript;

# Augmentation de la taille des fichiers envoyer
client_max_body_size 20M;

# Par défaut, les headers n'expirent pas
expires 0;

# On configure l'expiration des headers pour CSS et JS de la console. Ces fichiers 
# ont un nouveau timestamp à chaque version, il est donc raisonnable de les mettre en cache.
location ~ "console_.*\.(js|css)$" {
	expires max;
}

# Si une version en cache n'existe pas, on passe les requêtes vers des images à PHP
location ~ "^/storage/cache/images(/(([0-9]{3}/[0-9]{3})|custom)/.*)$" {
	# Les images en cache ont un timestamp dans l'URL, il est donc raisonnable
	# de les mettre en cache ici.
	expires max;
	try_files $uri /i.php?path=$1;
}

# On fait de même pour les fichiers de template .lens, Ceux-ci sont mis
# en cache lorsque c'est possible.
location ~ settings.css.lens$ {
	default_type text/css;
	try_files /storage/cache/site/${uri} /app/site/site.php?url=/settings.css.lens;
}

# Même chose pour le fichier koken.js
location ~ koken.js$ {
	default_type text/javascript;
	try_files /storage/cache/site/${uri} /app/site/site.php?url=/koken.js;
}

# Les requêtes standards sont mises en cache avec l'extension .html
set $cache_ext 'html';

# Les requêtes PJAX contiennent le paramètre GET _pjax et sont mises en cache 
# avec l'extension .phtml
if ($arg__pjax) {
	set $cache_ext 'phtml';
}

# On intercepte les requêtes à la racine
location ~ ^/?$ {
	try_files /storage/cache/site/index/cache.$cache_ext /app/site/site.php?url=/;
}

# Toutes les autres requêtes sont passées à Koken sauf si le fichier existe
location / {
	try_files $uri $uri/ /storage/cache/site/${uri} /storage/cache/site/${uri}cache.$cache_ext /app/site/site.php?url=$uri;
}
listen          443 ssl;

# Certificat et clé
ssl_certificate     /chemin/vers/mon/certificat.crt;
ssl_certificate_key /chemin/vers/ma/cle.key;
ssl_protocols       SSLv3 TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers         HIGH:!aNULL:!MD5;

# Si quelqu'un arrive sur la page en HTTP, pour ne pas avoir 
# la page d'erreur indiquant que l'on essaie d'accèder à un site 
# HTTPS en HTTP, on redirige directement vers la version HTTPS
error_page 497 = https://$host:443$request_uri;

Voilà ma configuration de base, encore une fois je débute avec Nginx et toute amélioration est la bienvenue, allez-y dans les commentaires ;]

(*) Le problème était le suivant : après un redémarrage du service PHP-FPM, tous les accès vers une page PHP renvoyaient une erreur 403, le reste du contenu statique était normalement accessible. J’ai retourné à peu près tout, entre autres la bonne configuration du paramètre security.limit_extensions = .php du pool (sous Fedora /etc/php-fpm.d/www.conf) et ce qui était fautif : le paramètre cgi.fix_pathinfo qui doit être à 1 dans le php.ini.

6 thoughts on “Ma configuration Nginx”

  1. Salut,
    Bien détaillé comme article, bravo!
    Je suis dans la même position que toi et cherche à basculer d’apache vers nginx. Pour l’heure j’essaye juste de faire cohabiter koken et owncloud, je migrerais wordpress plus tard. Le hic, c’est que j’y arrive pas malgré des heures de recherche et de lecture. Ta config me paraissant assez proche de la mienne, je me permet de te demander conseil : je ne vois pas d’include du koken.conf, j’en déduis qu’il est dans un ‘server block’ avec un server name dédié?
    Merci pour ton attention.

    1. Pour Owncloud, je ne m’y suis pas penché. Pour le reste, quel problème rencontres-tu?
      Pour le koken.conf et https.conf, j’ai juste ajouté ces morceaux de configuration pour info, ils sont inclus dans d’autres server blocks, pas dans celui de ce site.

      1. Ok Merci! Je pense que j’ai la réponse à mon problème c’est-à-dire pas de solution! owncloud ou pas, j’essaye de faire cohabiter koken en http avec un website en https sur un seul serveur (1 seul IP – celle de ma box) sans DNS associé pour l’heure.

        1. Ok tu n’as qu’une IP, donc tu ne peux pas avoir de virtual hosts. Il va falloir mettre tes applis web dans des sous-répertoires de ta racine web alors.
          Pour Koken pas de soucis, mais pour Owncloud, la dernière version que j’avais utilisé était la 4, donc ça date un peu. Mais c’est tout à fait possible j’imagine en définissant les bons location dans ton block server.

  2. Bonjour,
    Ton article date un peu mais j’ai vu que tu es sous Nginx. Avais-tu fait des Benchmark quand tu as franchi le pas d’Apache vers nginx? Pour quelle partie du site est-ce beaucoup plus rapide?

    Nginx a la réputation d’être plus rapide mais j’hésite vraiment car la config de mon site photo basé sur koken: http://www.jr-photo.be est pas mal optimisée.

    Je me demandais en fait si ça n’aurait pas du sens d’utiliser memcached+apache pour le contenu dynamique php et NGinx pour le contenu static comme une sorte de cdn local.

    Qu’en penses-tu?

    1. Hello Jonathan,
      Merci pour ton message. Non je n’ai pas fait de benchmark digne de ce nom, mais passer d’Apache à Nginx en général se voit directement. En plus, tu as un site photo, sous Koken, donc au final tu sers du contenu statique « lourd » (des photos), donc tu vas pleinement bénéficier de Nginx qui est spécialiste du contenu statique.
      Mon site photo (anciennement fabienamann.photography) est sous Koken aussi : https://photo.feub.net/ et même s’il pourrait être plus optimisé, il y a déjà de la différence avec Apache. Dans l’article tu as du voir les spécificités pour Koken, comme tu l’as dit qui datent un peu, mais comme j’ai quasiment arrêté la photo, je ne m’en suis plus trop soucié ;]
      J’utilise aussi Memcached.
      J’ai choisi de n’utiliser que Nginx en front, mais rien ne t’empêche comme d’autres le font de garder ton stack Apache et de mettre Nginx en front d’Apache.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *