Laisser un commentaire

Installer et optimiser Apache2

Apache2 c’est un peu le dinosaure du web. Le serveur de la fondation du même nom n’a plus à être présenté. Moderne, hyper configurable, puissant, quoi qu’on en dise, il reste un incontournable et répond à la plupart des besoins. Voyons tout de même en détail comme l’installer et l’optimiser.

Mesdames, Messieurs, oui, j’ai bien dit l’optimiser. On a l’habitude d’installer Apache avec un simple coup d’apt-get et puis s’en vont. Or s’il est vrai que la configuration par défaut suffit à bien des usages, il ne fait aucun mystère qu’une config plus fine apportera sécurité, rapidité et scalabilité.

Installation

Toutes les commandes exécutées ci-après sont exécutées en root. Pensez donc à passer en root avec sudo su ou à faire précéder vos commandes de sudo.

Le serveur Apache est bien entendu disponible dans les dépôts de Debian et d’Ubuntu. Pour l’installer, nous pouvons nous contenter d’un :

apt-get install apache2

Cependant, sur certaines distributions la version d’Apache proposée par les dépôts officiels est un peu vieille et ne permet pas de profiter du HTTP2 et d’autres fonctionalités récentes. Il faut Apache >= 2.4.17 pour le H2, ce qui est le cas pour Debian 8 et Ubuntu à partir de la LTS 16.04. Dans les autres cas, on passera par un ppa.

add-apt-repository ppa:ondrej/apache2
apt-get update
apt-get install apache2

On a ici au préalable ajouté un depôt “ppa” non officiel contenant les dernières versions d’Apache. Ce dernier dépôt est maintenu par le mainteneur de PHP des dépôts Debian. On peut donc s’y fier sans trop de craintes. Je n’ai pas vérifié, mais ça marche normalement aussi sur Debian.

C’est tout pour l’installation. Entrez maintenant l’adresse ip de votre serveur dans un navigateur, si tout s’est bien déroulé, vous devriez obtenir quelque chose de la sorte.

écran après installation d'Apache2
Page par défaut d'Apache2 sur Ubuntu
Le dossier dans lequel se trouvent les fichiers HTML par défaut se situe dans /var/www/html/ et le texte que vous venez de lire est tout simplement ce qui est contenu dans /var/www/html/index.html.

Bien qu’on puisse utiliser apache out of the box, en plaçant simplement nos fichiers html dans www/html/, nous allons explorer les options de configuration les plus courantes.

Création des VirtualHosts

L’hébergement virtuel (virtual hosting) est une méthode que les serveurs tels que serveurs Web utilisent pour accueillir plus d’un nom de domaine sur le même ordinateur, parfois sur la même adresse IP.

Wikipedia

Vous pouvez grâce à cette technique, héberger plusieurs sites sur le même serveur Apache. Attention cependant, pour qu’Apache puisse savoir vers quel site il doit vous rediriger, il faut que vous y accédiez via un nom de domaine, c’est grâce à ce dernier qu’il sera en mesure de vous présenter le bon site.

Ceci ne marchera donc pas si vous entrez directement l’adresse ip du serveur dans votre navigateur. Néanmoins, pour les besoins de tests, vous pouvez modifier votre fichier hosts pour simuler une résolution DNS.

Apache possède deux répertoires dans /etc/apache2/ dans lesquels on place nos VHosts : sites-available et sites-enabled. Le premier regroupe tous les sites disponibles, tandis que le second concerne les sites actifs.

Ainsi, Apache n’ira pas regarder les VHosts contenus dans sites-available. Cela permet donc de faire des tests, de temporairement retirer un site, de préparer un nouveau site ou une migration etc.

Lorsque tout est prêt, il suffit de faire un lien symbolique depuis sites-available vers sites-enabled. Apache fournit même une commande spécialement à cet effet : a2ensite nom_du_vhost ainsi que son pendant inverse a2dissite nom_du_vhost.

Créons maintenant notre premier VHost dans /etc/apache2/sites-available/buzut.conf.

# Apache doit écouter sur le port 80 pour le http
# il est aussi possible de répondre à des requêtes sur des ports non standards
# ou même de ne pas définir de port spécifique en utilisant "*"
<VirtualHost *:80>
    # nom d'hôte que le serveur utilise
    ServerName buzut.fr

    # noms alternatifs que le serveur utilise pour servir le même contenu
    ServerAlias www.buzut.fr blog.buzut.fr

    # email de contact de l'administrateur
    # apache l'affiche sur certaines pages d'erreurs
    ServerAdmin adresse@mail.com

    # path de la racine du contenu à servir
    DocumentRoot /var/www/buzut/

    # ces règles s'appliquent à la racine
    # et à tous les répertoires et fichiers descendants
    <Directory /var/www/buzut/>
        # interdiction de lister les fichiers dossier
        # au cas où pas de page index présente
        # suivre les liens symboliques (gain de performance)
        Options -Indexes +FollowSymLinks

        # .htaccess peut redéfinir ces paramètres [All|None]
        # on gagne en performance à mettre à none
        # on perd néanmoins en souplesse de configuration
        AllowOverride None

        # on précise que tous les utilisateurs ont accès au dossier web
        # ainsi, en cas de maintenance, on peut s'autoriser uniquement soi même
        Require all granted
    </Directory>

    # on peut néanmoins les redéfinir ou les compléter pour un répertoire particulier
    <Directory /var/www/buzut/>
        Options Indexes
    </Directory>
</VirtualHost>

VirtualHost et ServerName

Nous avons configuré ce VirtualHost pour répondre à toutes les requêtes sur le port 80 : *:80. Il est également possible de préciser une ip mais cela ne fait sens que dans le cas où le serveur sert des sites sur plusieurs ip publiques distinctes.

Par ailleurs, qu’il s’agisse d’ip ou de port, la directive VirtualHost ne modifie en aucun cas la configuration dictée par la directive Listen. Donc si le serveur n’est pas configuré pour écouter sur un port donné, le VHost mentionnant ce port ne sera d’aucune utilité.

En outre, le fait que ce VHost commence par *:80 ne signifie pas qu’il gère toutes les requêtes vers ce serveur sur ce port. Apache utilise le VirtualHost le plus spécifique correspondant à une requête pour y répondre. Pour configurer un autre VHost, il suffit de spécifier un autre bloc avec un ServerName différent. D’ailleurs, si vous n’avez qu’un VHost, ou pour en utiliser un par défaut, vous pouvez tout à fait omettre la directive ServerName.

Notez bien qu’un fichier de configuration de VHost peut contenir autant de directives VirtualHost que vous le souhaitez, donc autant de sites ou domaines différents. Mais il est tout de même recommandé de séparer les configurations de sites n’ayant rien à voir dans des fichiers différents. Cela vous permettra en outre de les activer et désactiver indépendamment les uns des autres.

DocumentRoot

DocumentRoot concerne la racine du site à servir. En théorie, Apache ne servira rien qui soit en dehors de ce path, et tout chemin défini dans l’url est relatif à cette racine. Si j’emploie le terme en théorie, c’est parce qu’il est possible avec les liens symboliques et avec la directive Alias de dire à Apache de servir des éléments se trouvant hors de la racine.

Revenons-en à notre root. Si celle-ci est définie à /var/www/buzut/ et que l’url accédée est https://buzut.fr/wp-content/themes/buzut-theme/img/logo.jpg, Apache ira chercher logo.jpg sur le serveur dans /var/www/buzut/wp-content/themes/buzut-theme/img/logo.jpg.

En outre, si l’url tente d’accéder à un répertoire, Apache tentera de servir le fichier défini par la directive DirectoryIndex (en général index.html, .php, .pl, ou .cgi).

Cette configuration par défaut peut bien entendue être modifiée au niveau du VHost : DirectoryIndex landing.html. Par ailleurs, si aucun de ces fichiers n’est présent, Apache tentera d’afficher un listing du répertoire si l’option Indexes est présente.

Vous l’aurez peut-être noté, mais si par défaut Indexes est désactivé dans notre racine (-Indexes), c’est que cette option permet de parcourir les fichiers et répertoires du serveur (dans la limite de ce qui est accessible publiquement). À moins que cela soit explicitement recherché, il n’y a pas lieu de laisser quiconque naviguer dans notre arborescence.

Directory

Cette directive permet de définir les règles qui s’appliquent à un répertoire, ses fichiers et sous-répertoires. Require permet de controler qui accède à ces ressources. Ici, tout le monde, Require all denied refuserait l’accès à tout le monde. On peut également n’autoriser que certaines addresses comme le souligne Eric dans les commentaires.

Require host my_alloweddomain.name
Require ip 192.168.0.110   # autorise une seule ip
Require ip 192.168.1.0 /24 # autorise 192.168.1.1 à 192.168.1.254
Require ip  172.16.0.0/16  # autorise 172.16.0.1 à 172.16.255.254
Require ip 10.0.0.0/8      # autorise 10.0.0.1 - 10.255.255.254

Pour en savoir plus sur ces directives, vous avez la doc à votre disposition.

Comme expliqué dans les commentaires de l’exemple, il est possible d’appliquer un comportement particulier à un sous-répertoire en définissant de nouvelles règles dans une directive Directory plus spécifique.

Le AllowOverride dont il est fait mention permet à un fichier .htaccess de redéfinir les règles s’appliquant à un répertoire directement depuis celui-ci. Il suffit pour cela de placer les règles s’appliquant à Directory dans un fichier nommé .htaccess que l’on place dans le répertoire en question.

Sachez toutefois que lorsque cette option est activée, Apache doit vérifier pour chaque répertoire si un fichier .htaccess est présent. Dans le cas d’arborescences profondes, cela peut générer un travail supplémentaire non négligeable pour Apache et impacter les performances.

Les modules complémentaires

Selon les besoins de votre site, il vous faudra peut-être activer des modules supplémentaires. Par exemple, si vous utilisez l’URL-rewriting, il faudra activer le module mod_rewrite :

a2enmod rewrite
Enabling module rewrite.
To activate the new configuration, you need to run:
  service apache2 restart

Il nous est dit qu’il faut redémarrer le serveur : To activate the new configuration, you need to run: service apache2 restart. Alors redémarrez-le.

Apache possède de très nombreux modules qui viennent élargir ses fonctionnalités. Vous trouverez sur le site officiel la liste de tous les modules disponibles.

Puisque nous parlons des modules, nous allons aussi en désinstaller quelques uns dont nous n’avons pas l’utilité. Une bonne politique dans la gestion d’un serveur est que tout ce qui ne sert pas peut être désactivé et supprimé. D’une part ceci nous permet d’économiser des ressources (les modules d’apache prennent de la place en mémoire), d’autre part, ce sont autant de failles potentielles que l’on supprime.

En ce qui me concerne, je ne vire que le mod CGI (et il sera utile pour ceux utilisant PHP-FMP), la plupart de ceux pre-activés servent souvent.

Vous utiliserez pour désactiver les mods la commande a2dismod suivi du nom du module à désactiver. Ex : a2dismod autoindex. Il peut être judicieux de ne pas tous les supprimer d’un coup et de tester après chaque désactivation si tout marche bien.

Vous pouvez aussi utiliser la commande apache2ctl configtest qui vérifie la syntaxe de vos fichiers de config. Ça peut s’avérer intéressant si par exemple vous désactivez un mod dont vous avez besoin et auquel vous faites appel dans vos fichiers dans /etc/apache2/sites-enabled.

Note : je dis supprimer, mais ne supprimez pas le fichier du module, ça ne sert à rien pour le peu d’espace que vous allez gagner, et si vous en avez besoin pour une raison x ou y plus tard, vous serez content de n’avoir qu’à le réactiver.

Au besoin, vous trouverez dans mon article regroupant les commandes Linux les plus utiles, une partie web qui reprend l’ensemble des commandes Apache utiles.

Par ailleurs, si vous souhaitez exécuter du php, il va falloir l’installer aussi. Sans lui, notre serveur Apache servira vos fichiers de code php tels quels. Admettons-le, c’est moyen…

SSL/TLS et HTTP/2

SSL/TLS, je ne pense pas avoir besoin de l’expliquer. Mais HTTP/2, qu’est-ce que c’est ? Tout simplement la dernière version du protocole de la norme HTTP. Cette version apporte de nombreux avantages dont un sérieux gain en performances.

Réduction de la latence pour améliorer les temps de chargement des navigateurs web notamment via :

  • la compression de données des en-têtes HTTP
  • le nouveau mécanisme de push de HTTP/2 permettant au serveur d’envoyer au client des données nécessaires mais qu’il n’a > pourtant pas encore sollicitées
  • le nouveau mécanisme de pipelining des requêtes, rapprochable à ce qui se fait pour les processeurs
  • Résolution de problèmes propres à HTTP 1.x (voir HTTP pipelining ou head-of-line blocking)
  • Multiplexage de plusieurs requêtes au travers d’une seule connexion TCP

Wikipedia

HTTP/2 ne fonctionne qu’en TLS. De toute façon, avec l’émergence de Let’s Encrypt et ses certificats TLS gratuits, nous n’avons plus aucune excuse pour ne pas offrir une connexion sécurisée à nos sites.

Pour installer un certificat Let’s encrypt, je vous laisse entre les mains de la doc officielle.

Vous voilà en possession de vos certificats, voyons donc à quoi ressemble notre VHost TLS et HTTP/2.

# on redirige le http vers le https
<VirtualHost buzut.fr:80>
    ServerName buzut.fr
    ServerAdmin adresse@mail.com
    Redirect permanent / https://buzut.fr/
</VirtualHost>

# le port par défaut du https est 443, on pense donc bien à le spécifier
<VirtualHost buzut.fr:443>
    # on active le http/2 avec fallback vers le http/1.1
    # tous les navigateurs ne sont pas compatibles
    Protocols h2 http/1.1
    ServerName buzut.fr

    ServerAdmin adresse@mail.com
    DocumentRoot /var/www/buzut

    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined

    # on active le ssl/tls et on renseigne les certificats
    SSLEngine on

    # et on renseigne les path vers les certificats
    #
    # Fichier de données contenant le certificat X.509 du serveur codé en PEM
    SSLCertificateFile /etc/letsencrypt/live/buzut.fr/cert.pem

    # Fichier contenant la clé privée du serveur codée en PEM
    SSLCertificateKeyFile /etc/letsencrypt/live/buzut.fr/privkey.pem

    # Fichier contenant les certificats de CA du serveur codés en PEM
    SSLCertificateChainFile /etc/letsencrypt/live/buzut.fr/chain.pem

    # on active HSTS pour 6 mois pour le domaine et les sous-domaines
    # on autorise aussi l'ajout du site dans les listes pre-chargées
    Header always set Strict-Transport-Security "max-age=15768000; includeSubDomains; preload"
</VirtualHost>

Pour activer TLS, il faut donc trois choses :

Par ailleurs, les modules permettant le support du TLS et de http2 ne sont pas forcement activés. Pour ce faire, on fera simplement un petit coup de a2enmod :

a2enmod ssl && a2enmod http2
service apache2 restart

Nous nous sommes ici contenté d’activer le TLS (bien que le module s’appelle SSL !). Cependant, si la sécurité vous intéresse, il est possible d’affiner la configuration du module TLS. Vous pouvez d’ailleurs tester votre serveur avec ce scanner SSL/TLS et générer vos configurations serveurs avec le générateur de Mozilla.

Tout ce qui ne concerne pas directement le vhost trouve sa place directement dans le module ssl situé dans /etc/apache2/mods_available/ssl.conf.

Si vous souhaitez en savoir plus sur le HSTS, je vous laisse avec l’article Wikipedia qui est bien détaillé.

Les fichiers de log

Par défaut, apache écrits ses logs dans /var/log/apache2. Il y a trois fichiers distincts :

En réalité, la définition du fichier de log des accès se fait dans le VHost. Si vous voulez écrire vos logs dans le access.log au lieu du other_vhosts_access.log, ou même définir vous-même le nom de fichier de log, il va vous falloir rajouter une ligne dans le VHost.

CustomLog ${APACHE_LOG_DIR}/access.log combined

Vous pouvez remplacer access.log par le nom qui vous plaît si vous voulez en changer. Quant à combined, cela concerne le format des logs. Pour en savoir plus à ce sujet, je vous invite à aller consulter la doc.

Petit point de détail cependant, si vous ne spécifiez pas explicitement le access.log dans votre VHost, les logs iront automatiquement dans other_vhosts_access.log. Ce comportement peut ne pas convenir.

Nous pourrions par exemple vouloir ne pas activer les logs d’accès (pour des questions de performances par exemple). Dans ce cas, il va falloir désactiver une configuration. Laissez-moi donc introduire la commande a2disconf. Vous l’aurez deviné, elle va de paire avec a2enconf. Pour désactiver le other_vhosts_access.log, il faut donc exécuter :

a2disconf other-vhosts-access-log

Enfin, pour activer ce petit VHost, nous allons désactiver le site default, et activer le notre. Pour se faire, nous allons utiliser deux commandes :

a2dissite default
Site default disabled.
To activate the new configuration, you need to run:
  service apache2 reload

a2ensite mon_site
Enabling site default.
To activate the new configuration, you need to run:
  service apache2 reload

service apache2 reload
 * Reloading web server config apache2
   ...done.

Le serveur nous spécifie la commande à effectuer pour que les modifications soient prisent en compte. Il n’est pas ici nécessaire de redémarrer apache, un simple rechargement avec la commande service apache2 reload suffit.

Sécurité

Les CHMOD

Afin de bien sécuriser son serveur, il ne s’agit pas simplement de “balancer” les fichiers sur le serveur. Il faut accorder une vigilance toute particulière aux droits que possèdent ces fichiers.

La base consiste à donner le moins de droits possible aux fichiers et répertoires. Ainsi, le chmod devra être à 555 pour les répertoires et à 444 pour les fichiers. Si le propriétaire des répertoires et fichiers est le compte du développeur et non le serveur web, il peut être pratique de passer à respectivement 755 et 744. De cette manière, vous pourrez éditer vos fichiers sans passer en root.

J’ai écrit un article détaillé au sujet des CHMOD dans le contexte de l’hébergement web. N’hésitez pas à vous y référer pour plus de détails.

Le fichier de configuration security

Il existe un fichier de configuration relatif à la sécurité d’apache : /etc/apache2/conf-available/security. Les réglages qu’il apporte ne sont en réalité pas déterminants pour la sécurité de notre serveur. Voyons néanmoins les paramètres qu’il contient :

On va donc placer ServerTokens à Prod, ServerSignature à Off et TraceEnable à Off. C’est tout pour ce fichier.

Empêcher de parcourir les fichiers

Au niveau de la configuration de votre site, il est prudent de préciser quelques paramètres concernant la racine de fichiers :

<VirtualHost *:80>
    ServerAdmin contact@monsite.fr
    DocumentRoot /var/www

    <Directory />
        # empêche un fichier .htaccess de redéfinir ces paramètres
        AllowOverride None

        # empêche de parcourir le syteme de fichiers
        # il faudra donc bien placer cette directive à granted
        # dans le(s) répertoire(s) contenant votre/vos site(s)
    Order allow,deny
        Require all denied
    </Directory>

    <Directory /var/www/monsite/>
        …
    </directory>
</VirtualHost>

Les modules complémentaires relatifs à la sécurité

Parmi les modules d’Apache, certains permettent d’améliorer la sécurité. Je n’expliquerai pas ici leur fonctionnement, parce que cela nécessiterait un article complet, mais je vais vous donner les liens qui vont bien.

Pour ma part, je ne les utilise pas. Vous pouvez installer fail2ban pour améliorer la sécurité de votre serveur. Le pare-feu iptables, vous permet de bien vous prémunir contre les attaques DDoS.

Optimisation des performances

On parle beaucoup de Nginx dès lors que l’on mentionne les performances dans le web. Je dédis d’ailleurs moi même un article à ce sujet. Cependant, Apache reste un serveur extraordinaire et qui sait se montrer très performant.

On commence rapidement par vérifier quel est le module MPM utilisé, à savoir, prefork, worker ou event.

Le plus vieux des trois est le MPM prefork, ce dernier utilise un processus par connexion. Par conséquent, il est plus vorace en RAM et moins adapté aux serveurs devants faire face à de très nombreuses connexions simultanées. En revanche, il est thread-safe. Il est également le seul à supporter le mod_php. Les autres modes devront utiliser php-fpm.

Le plus récent des trois modes est le MPM event. Il reprend le principe du MPM worker – à savoir un modèle hybride multi-processus et multi-threads – et l’améliore pour mieux gérer la saturation des connexions dû à keep-alive. Sauf raison spécifique, on utilisera de préférence le dernier ; d’autant plus que le MPM prefork ne supporte pas le HTTP/2.

# affiche les modules disponibles
apache2ctl -M

Bien configuré, le MPM prefork peut être très performant. Aussi, pour des raisons de compatibilité, je détaille également l’optimisation de ce dernier. Néanmoins, prefork ne supporte pas le HTTP/2, on privilégiera donc le MPM event autant que possible.

MPM prefork & event

Les optimisations concernant le MPM prefork se trouvent dans /etc/apache2/mods-enabled/mpm_prefork.conf tandis que celles relatives à event se trouvent dans /etc/apache2/mods-enabled/mpm_event.conf.

MaxRequestWorkers

Permet de déterminer le nombre maximal de requêtes pouvant être traitées simultanément. Au delà, toutes les requêtes iront dans une file d'attente.

Pour le prefork, cela correspond au nombre maximum de processus enfants qui pourront être lancés. Pensez que cette valeur est limitée par ServerLimit (cette directive se définit dans le fichier de configuration globale d'Apache). Il vous faudra donc l'augmenter si vous voulez fixer un nombre de workers plus grands.

Dans le cas du MPM event, MaxRequestWorkers fait référence au nombre global maximum de threads. Étant donné que chaque processus possède un nombre de threads donné – fixé par la directive ThreadsPerChild que nous verrons plus bas – le nombre total de processus est définit par MaxRequestWorkers / ThreadsPerChild et ne peut dépasser ServerLimit.

Étant donné que pour le MPM prefork, la RAM consitue la limite, voici comment définir le nombre : (RAM totale - RAM utilisée par les autres processus) / taille moyenne d'un processus Apache. Munissez-vous de top et ps aux pour cette mission ! Et si vous être vraiment des gros flemmards, j'ai la formule magique qu'il vous faut pour calculer le poids moyen d'un process Apache :


# grep apache pour Ubuntu / grep httpd pour Debian
ps aux | grep apache | awk '{print $6/1024;}' | awk '{avg += ($1 - avg) / NR;} END {print avg " MB";}'
38.1419 MB
    
MaxConnectionsPerChild

Fixé par défaut à 0 (infini), cette directive permet de faire "mourir" un processus après qu'il ait servit un certain nombre de requêtes. Cela peut-être intéressant dans le cas de fuites mémoires. Par exemple, avec mod_php, si les processus ont tendances à avoir une emprunte mémoire qui grossit indéfiniment, on pourra les limiter à un certain nombre de requêtes et un nouveau prendra la relève. Comme la création de nouveaux processus consomme du CPU, il s'agit de trouver le juste équilibre.

StartServers

Cette directive, qu'on utilise en général conjointement avec MinSpareServers et MaxSpareServers, spécifie le nombre de processus enfants qu'Apache doit lancer au démarrage.

La création et l'arrêt de processus est dynamique. Cependant, dans le cas de sites à très fort trafic, il peut être judicieux de démarrer directement avec de nombreux processus pour pouvoir affronter la charge sans un temps de warm up. Aussi, on jouera avec les directives (min|max)SpareServers afin de :

  • toujours avoir un minimum de processus enfants prêts à réceptionner des requêtes,
  • ne pas tuer des processus lors d'une baisse momentannée de trafic alors qu'une autre vague risque d'arriver, obligeant une couteuse opération de killing/re-creation.

Dans le MPM event, on ne raisonne pas en processus mais en threads. Ces deux directives dont donc remplacées par MinSpareThreads et MaxSpareThreads.

ThreadsPerChild

Il s'agit du nombre de threads lancé par chaque enfant. Chaque processus enfant créé ses threads au démarrage et n'en créé plus par la suite. Cette valeur ne peut dépasser ThreadLimit. En outre, si le nombre de threads est trop important, cela peut rendre le système instable.

ThreadLimit

Cette directive fixe la valeur maximum qu'il est possible d'allouer à ThreadsPerChild. Tandis que ThreadsPerChild peut être redéfinie via un reload, il faudra impérativement arrêter puis le re-démarrer le serveur si l'on veut modifier ThreadLimit.

Par ailleurs, allouer une valeur inutilement importante et bien au delà de ThreadsPerChild aboutira à une consommation mémoire plus importante.

Configuration générale

Ces optimisations ne concernent pas un MPM en particulier, elles se trouvent dans /etc/apache2/apache2.conf ou httpd.conf sur Debian.

KeepAlive & KeepAliveTimeout

Cette option offerte par le HTTP/1.1 permet de servir plusieurs requêtes dans la même connexion TCP. Ainsi, on garde littéralement ouverte une connexion TCP afin de pouvoir servir de nouvelles requêtes en évitant de perdre du temps en refaisant un handshake TCP. C'est particulièrement utile pour servir tous les assets d'une page sans ouvrir 10000 connexions (typiquement JS, CSS, images).

C'est bien beau tout cela, mais notre petit KeepAlive bousille littéralement la capacité d'Apache de faire face à la charge. On l'a vu, Apache est limité par MaxRequestWorkers, or si on conserve les connexions ouvertes, forcement, on peut moins en servir en même temps…

KeepAlive permet quand même d'améliorer l'expérience utilisateur sur les connexion à forte latence (mobile et connexion géographiquement éloignées Europe/USA par exemple). De même, cela permet d'améliorer les performances dans le cas de polling Ajax.

Néanmoins, l'intérêt baisse quelque peu avec le HTTP/2 car il permet le multiplexage et le pipelining. Faites vos calculs, mais si vous avez un peu de charge, il est conseillé de clairement abaisser les 30 secondes par défaut.

FollowSymLinks

Nous l'avons déjà mentionné plus haut, mais il vaut mieux préciser cette option. En son absence, ou dans le cas où il est explicitement indiqué de ne pas les suivre, Apache devra faire des appels système supplémentaires afin de vérifier si le pointeur est un lien symbolique ou non. De plus, dans la mesure où le résultats ne sont pas mis en cache par Apache, c'est le bingo à chaque requête.

AllowOverride

Nous l'avons déjà mentionné aussi. Cette directive permet d'activer les fichiers .htaccess, grande spécificité de Apache. Ces petits fichiers de configuration permettent une grande souplesse car ils offrent la possibilité de configurer le comportement de sous-répertoires directement depuis ces derniers.

Ainsi, il est possible de laisser les utilisateurs ou les développeurs gérer eux-mêmes le comportement des répertoires dont ils sont responsables (rewriting etc). Dans une équipe où développeurs et administrateurs ont des rôles bien différents, cela laisse plus de latitudes aux développeurs.

Néanmoins, c'est au sacrifice d'un peu de performances puisque Apache devra vérifier dans chaque répertoire le présence ou non d'un fichier de config, et le lire le cas échéant.

DirectoryIndex

Cette directive permet de spécifier le fichier à servir par défaut lors de la requête d'un répertoire. Typiquement la racine d'un site par exemple : buzut.fr et non pas buzut.fr/index.php. Apache sert donc automatiquement index.php lorsqu’on requête ce répertoire.

Il est possible – et on le voit souvent – de ne spécifier que le nom de base sans l'extension index. Cela permet à Apache de servir index.php, index.html, index.pl etc. On gagnera en performance à spécifier les types, et il est possible d'établir une liste. Dans ce cas, les fichiers à gauche seront servis en priorité sur ceux à droite dans la liste.

DirectoryIndex index.php index.html index.pl
    

Sachant tout cela, après avoir laissé tourner le serveur quelques temps, il peut être judicieux de lancer un script qui analysera la config et différents paramètres afin de vous fournir quelques voies d’optimisations. Le script en question s’appelle apache2buddy et pour le lancer, rien de plus simple :

curl -L http://apache2buddy.pl/ | perl

Compression

Par défaut, Apache compresse certains types de fichiers avant de les envoyer. Les fichiers sont ainsi plus légers et le temps de transfert est réduit. Le navigateur les décompresse à la volée et voilà.

Ceci est géré par le module deflate qui compresse le tout en gzip (ou deflate, selon la demande du navigateur). Dans 99% du temps vous n’avez rien à faire. Cependant, il y a un type de media non compressé par défaut et qui peut pourtant avantageusement en tirer profit, il s’agit des images svg.

Pour permettre à Apache de les compresser à la volée, rendez-vous dans le fichier de configuration dudit module : /etc/apache2/mods-available/deflate.conf

<IfModule mod_deflate.c>
    <IfModule mod_filter.c>
        AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css
        AddOutputFilterByType DEFLATE application/x-javascript application/javascript application/ecmascript
        AddOutputFilterByType DEFLATE application/rss+xml
        AddOutputFilterByType DEFLATE application/xml

        # j'ai ici ajouté le type MIME du svg et du json
        AddOutputFilterByType DEFLATE image/svg+xml
        AddOutputFilterByType DEFLATE application/json
    </IfModule>
</IfModule>

On recharge ensuite la configuration avec un savant coup de service apache2 reload et en voiture Simone !

Nous l’avons dit, Apache compresse à la volée, ce qui signifie qu’à chaque requête, les fichiers sont compressés avant envoi. On comprends dès lors qu’il faut trouver le juste milieu entre taille du fichier et temps de compression. En effet, si le fichier est trop long à compresser, peut-être est-ce plus rapide de ne pas le compresser, ou de moins le compresser.

La compression à la volée fait tout à fait sens pour les contenus générés dynamiquement. Cependant, dans le cas de fichiers statiques (html brut, JavaScript, CSS…), il est possible de compresser en amont et d’indiquer à Apache de servir ces fichiers déjà compressés si le navigateur les supporte. Cela permet en outre d’utiliser les taux de compression les plus élevés.

Vous avez peut-être entendu parler de Brotli. Il s’agit d’un format de compression mis au point en 2015 par Google qui offre des taux de compressions bien supérieurs à Gzip. Étant donné que la quasi totalité navigateurs le prennent en charge, nous n’allons pas nous priver de pré-compresser aussi en brotli.

En revanche, deflate étant beaucoup moins demandé, nous n’allons pas nous en préoccuper. En dernier recours, si rien d’autre n’est supporté, Apache utilisera la compression à la volée.

La compression étant en amont, ce n’est pas Apache qui la gère. On peut donc l’effectuer en local, lors du processus de build par exemple ou lors du deploy. Une fois que tous ces fichiers sont sur le serveur, on va spécifier à Apache via quelques règles de réécriture de les servir avec les bons en-têtes.

# Déclare le type MIME brotli
# pour les fichiers que l'on va servir en brotli
# vous pouvez ajouter le xml, json et tout fichier à base de texte si pertinent
<Files *.js.br>
    AddType "application/javascript; charset=utf-8" .br
    AddEncoding br .br
</Files>
<Files *.css.br>
    AddType "text/css; charset=utf-8" .br
    AddEncoding br .br
</Files>
<Files *.svg.br>
    AddType "image/svg+xml; charset=utf-8" .br
    AddEncoding br .br
</Files>
<Files *.html.br>
    AddType "text/html; charset=utf-8" .br
    AddEncoding br .br
</Files>

# Idem pour le gzip
<Files *.js.gz>
    AddType "application/javascript; charset=utf-8" .gz
    AddEncoding gzip .gz
</Files>
<Files *.css.gz>
    AddType "text/css; charset=utf-8" .gz
    AddEncoding gzip .gz
</Files>
<Files *.svg.gz>
    AddType "image/svg+xml; charset=utf-8" .gz
    AddEncoding gzip .gz
</Files>
<Files *.html.gz>
    AddType "text/html; charset=utf-8" .gz
    AddEncoding gzip .gz
</Files>

# on démarre le moteur de réécriture
RewriteEngine on
RewriteBase /

# Transmet les fichiers pré-compressés en brotli
RewriteCond %{HTTP:Accept-Encoding} br
RewriteCond %{REQUEST_FILENAME}.br -f
RewriteRule ^(.*)$ $1.br [L]

# Transmet les fichiers pré-compressés en gzip
RewriteCond %{HTTP:Accept-Encoding} gzip
RewriteCond %{REQUEST_FILENAME}.gz -f
RewriteRule ^(.*)$ $1.gz [L]

Vous avez peut-être remarqué que l’on définit les contenus comme étant encodés en utf-8. C’est en effet aujourd’hui presque un standard. Cependant, si vos contenus sont encodés différemment, adaptez les réglages.

Dans le doute, charset est optionnel et vous pouvez l’omettre. Dans ce cas, le navigateur du client tentera de deviner l’encodage du fichier reçu.

HTTP/2 push

Nous avons vu dans la section sur le HTTP/2 comment activer ce dernier sur le serveur. Cette version du protocole permet de faire du push, c’est à dire d’envoyer au navigateur des éléments qu’il n’a pas encore demandé. Cela permet par exemple de lui envoyer le CSS et le JS avant même qu’il ne les demande, on gagne donc le temps du parsing. On peut mettre en place cela très facilement avec un en-tête.

Néanmoins, comme on envoie des éléments sans l’avis du navigateur, cela court-circuite le cache de ce dernier. Il ne faudrait donc pas envoyer des éléments qu’il possède déjà, au risque de gaspiller des ressources réseau. On va alors lui transmettre les éléments seulement s’il n’y a aucun cookie d’envoyé. Cela signifie qu’il s’agit probablement de la première visite sur le site.

Cette technique implique évidemment que votre site créé des cookies. Même sans le faire explicitement, de nombreux scripts embarqués dans vos pages créent des cookies (Google Analytics par exemple). Si ce n’est pas le cas, vous pouvez demander à Apache d’en créer un.

# Pour tous les fichiers .html ou html gzippé ou brotlisé
# on balance automatiquement le css et le js s'il n'y a pas de cookie
<Files *.html.br>
    Header add Link "</styles/styles.min.css>;rel=preload" "expr=-z %{req:Cookie}"
    Header add Link "</js/scripts.min.js>;rel=preload" "expr=-z %{req:Cookie}"
</Files>

<Files *.html.gz>
    Header add Link "</styles/styles.min.css>;rel=preload" "expr=-z %{req:Cookie}"
    Header add Link "</js/scripts.min.js>;rel=preload" "expr=-z %{req:Cookie}"
</Files>

<Files *.html>
    Header add Link "</styles/styles.min.css>;rel=preload" "expr=-z %{req:Cookie}"
    Header add Link "</js/scripts.min.js>;rel=preload" "expr=-z %{req:Cookie}"
</Files>

That’s it pour le push !

On en a fini avec la configuration d’Apache. Je vous laisse quand même avec la doc officielle (qui est bien conçue et en français), au cas où vous voudriez creuser certains points.

Commentaires

Rejoignez la discussion !

Vous pouvez utiliser Markdown pour les liens [ancre de lien](url), la mise en *italique* et en **gras**. Enfin pour le code, vous pouvez utiliser la syntaxe `inline` et la syntaxe bloc

```
ceci est un bloc
de code
```