molitor.photography

Directive Require : contrôler l'accès aux répertoires sans mod_access_compat

2026-11-13

Si vous avez déjà vu un serveur Apache répondre « 403 Forbidden » à des utilisateurs qui auraient dû être autorisés, vous connaissez probablement le sujet de cet article. Depuis la version 2.4, le contrôle d'accès repose sur la directive Require, et cette transition déroute encore beaucoup d'administrateurs en 2026. Le réflexe le plus courant consiste à charger mod_access_compat pour continuer à utiliser Order, Allow et Deny. Ça fonctionne, mais c'est une béquille. Je vais vous montrer comment vous en passer, avec une logique plus propre, plus lisible et plus sûre. Accrochez-vous, on va décortiquer le mécanisme ensemble.

Le contrôle d'accès n'est pas un détail cosmétique. C'est la première ligne de défense d'un site, d'un back-office ou d'un intranet. Une règle mal écrite et c'est tout un répertoire qui devient public, ou l'inverse. Et le paradoxe de la 2.4, c'est que la nouvelle syntaxe est en réalité plus simple que l'ancienne, à condition d'accepter de la comprendre au lieu de la contourner. Alors prenons quelques minutes pour voir comment Require fonctionne, ce qu'elle change, et comment composer des règles fiables sans jamais dépendre du module de compatibilité.

Pourquoi la directive Require remplace tout ce que vous connaissiez

Dans la 2.2, le contrôle d'accès passait par la triade Order, Allow, Deny. Il fallait mémoriser l'ordre d'évaluation (« deny,allow » ou « allow,deny »), puis empiler des règles d'adresses IP, avec des interactions parfois contre-intuitives. La 2.4 a tout remis à plat avec un modèle de fournisseurs : chaque clause Require désigne un type de vérification fourni par un module, et le serveur évalue la requête selon ce fournisseur. Fini l'ordre à retenir par cœur, place à des règles explicites et composables.

De Order/Allow/Deny à la logique par fournisseurs

Concrètement, à chaque nœud de la configuration qui accepte les directives d'autorisation, le serveur consulte le module mod_authz_core et les modules d'autorisation chargés. Chaque module propose des fournisseurs comme ip, host, user, group ou method. Une clause Require appelle un fournisseur et lui fournit les arguments. Le résultat est évalué, et l'accès est accordé ou refusé selon un ensemble de règles. C'est à la fois plus modulaire et plus prévisible. Et comme chaque fournisseur est documenté séparément, on comprend immédiatement ce qu'on fait.

Ce que change réellement la 2.4 dans votre httpd.conf

Le plus gros changement n'est pas la syntaxe en elle-même, c'est la logique de combinaison. Avec Order, la combinaison était implicite et dépendait de l'ordre. Avec Require, elle est explicite grâce aux blocs <RequireAll>, <RequireAny> et <RequireNone>. Vous décidez réellement de la logique booléenne, ce qui est une amélioration significative. Et comme ces blocs peuvent s'imbriquer, vous pouvez exprimer des règles complexes qui auraient été un cauchemar en 2.2. Le revers de la médaille, c'est qu'il faut un peu de rigueur, sinon on crée des contradictions invisibles.

Comprendre le fonctionnement de Require en 2.4

Avant d'écrire des règles, il faut savoir quand elles sont évaluées et par qui. La directive Require intervient pendant la phase d'autorisation, après l'authentification éventuelle. Le module qui fournit le fournisseur peut consulter la requête, l'utilisateur authentifié, son groupe ou son adresse IP. Si l'authentification est requise mais qu'aucun mécanisme n'a fourni d'identité, l'accès est refusé. Ce détail compte, parce qu'il explique certains 401 en apparence mystérieux. Le 401 vous dit qu'il faut d'abord s'authentifier, le 403 vous dit que même authentifié, vous n'êtes pas autorisé.

Le cycle autorisation : quand la directive est évaluée

Le serveur traverse les blocs de configuration applicables à la requête, détermine les règles actives, puis demande à chaque clause Require de rendre son verdict. Si plusieurs clauses sont présentes sans bloc englobant, elles sont traitées selon une logique implicite qui dépend de leur type : les clauses positives sont combinées comme un « ou », et une clause négative, précédée de not, impose un « et ». Cette subtilité est une source classique d'erreurs, et c'est pour cela que je recommande de toujours envelopper les clauses dans un bloc explicite. Au moins, ce que vous lisez est ce qui s'applique.

Les fournisseurs : ip, host, user, group, method, all

Les fournisseurs les plus utilisés en pratique sont peu nombreux, et les connaître vous couvre 95 % des cas :

  • ip : compare l'adresse IP du client à une liste d'adresses ou de plages CIDR
  • host : résout le nom d'hôte du client et le compare à un domaine
  • user : vérifie l'utilisateur authentifié contre une liste de noms
  • group : idem, mais au niveau du groupe fourni par le mécanisme d'authentification
  • method : autorise ou refuse selon la méthode HTTP (GET, POST, PUT, DELETE…)
  • all : fournisseur spécial qui accepte granted ou denied et court-circuite les autres

Il existe d'autres fournisseurs, comme env lié aux variables d'environnement, ou expr qui accepte une expression complexe. Vous les croiserez moins souvent, mais ils sont utiles dans des cas particuliers. Notez que host repose sur une résolution DNS inverse, ce qui peut être lent et parfois peu fiable en fonction de la configuration réseau. Pour tout ce qui est IP, je préfère ip, plus déterministe.

Le contexte autorisé et les pièges de placement

Require fonctionne dans les contextes directory et .htaccess, c'est-à-dire à l'intérieur d'un bloc <Directory>, <Location>, <Files> ou d'un fichier distribué. Elle ne s'applique pas en contexte global serveur ou serveur virtuel direct. Cette restriction surprend parfois, mais elle est logique : une règle d'accès définit une zone, pas l'ensemble d'un serveur. Si vous voulez appliquer une règle à tout un hôte, il faut passer par un bloc <Location /> ou un bloc <Directory> qui couvre sa racine. Et n'oubliez pas que la portée dépend de AllowOverride si vous écrivez dans un .htaccess.

Les règles de base à maîtriser

Une fois le mécanisme compris, les règles de base deviennent faciles à écrire. Je les utilise tous les jours, et elles couvrent la grande majorité des besoins. Le secret, c'est de rester explicite, sans jamais compter sur des comportements implicites. Une règle claire, c'est une règle qui survivra à la prochaine relecture par un collègue un peu pressé.

Require all granted : ouvrir un répertoire proprement

La clause Require all granted autorise l'accès à tous les clients, quelle que soit leur adresse ou leur identité. C'est la règle par défaut implicite pour les répertoires qui ne sont pas protégés, mais l'écrire explicitement est souvent une bonne idée dans une configuration où d'autres règles existent. Cela évite que d'autres blocs, plus généraux, ne s'appliquent par héritage. Je l'ajoute systématiquement dans les blocs où je veux un comportement clair et sans ambiguïté. C'est un peu verbeux, mais c'est lisible et sans surprise.

Require all denied : verrouiller par défaut

À l'inverse, Require all denied refuse tout accès, et c'est une excellente base de départ pour sécuriser un répertoire. On commence par tout fermer, puis on ouvre seulement ce dont on a besoin. Cette approche par défaut restrictif est une pratique de sécurité robuste : elle garantit qu'une omission n'ouvre pas une porte. Le revers, c'est qu'il faut documenter les ouvertures, sinon un futur administrateur cherchera longtemps pourquoi un client est bloqué. Un commentaire d'une ligne au-dessus de chaque exception suffit la plupart du temps.

Require ip, Require host et Require local

Require ip 192.168.1.0/24 autorise une plage d'adresses, Require ip 203.0.113.42 une adresse précise, et la directive accepte plusieurs adresses séparées par des espaces. Require host example.com filtre par nom d'hôte résolu, mais comme évoqué plus haut, cela dépend d'une résolution DNS inverse qui n'est pas toujours fiable. Pour le cas spécifique local, Require local est plus pratique qu'une liste d'adresses en dur : elle autorise les requêtes provenant de la machine elle-même, ce qui convient bien aux pages de statut ou aux scripts internes. En 2026, je vois de plus en plus d'articles en anglais sur ce sujet, mais très peu en français avec des exemples concrets. D'où cet article.

Require user et Require group avec mod_authn_file

Pour l'authentification par mot de passe, il faut d'abord un module qui fournit les utilisateurs, généralement mod_authn_file pour un fichier de type htpasswd, puis une clause Require user ou Require group. La directive AuthUserFile pointe vers le fichier, et AuthGroupFile vers la liste des groupes. Tout ça tient en quatre ou cinq lignes. Le piège ici, c'est de croire que l'authentification suffit : elle ne fait qu'identifier l'utilisateur, et c'est Require qui décide ensuite de l'autorisation. Si vous oubliez la clause Require, l'accès est généralement refusé, ce qui peut surprendre quand on vient de la 2.2.

Composer des règles avec RequireAll, RequireAny et RequireNone

C'est ici que la 2.4 prend toute sa valeur. Les blocs <RequireAll>, <RequireAny> et <RequireNone> permettent d'exprimer des combinaisons logiques explicites. On peut même les imbriquer pour aller plus loin. Ce mécanisme remplace les subtilités de l'ancien couple Satisfy et Order, avec une clarté incomparablement supérieure. Une fois qu'on a pris l'habitude, on ne revient jamais en arrière.

La logique booléenne expliquée simplement

<RequireAll> exige que toutes les clauses internes soient vérifiées, comme un « ET » logique. <RequireAny> exige au moins une clause vraie, comme un « OU ». <RequireNone> bloque l'accès dès qu'une clause interne est vraie, ce qui correspond à un « NON ». Si une clause interne est une négation, par exemple Require not ip, elle inverse sa propre valeur. Cette grammaire simple permet de décrire précisément la règle métier. Et contrairement à l'ancien système, l'ordre des blocs internes n'a pas d'influence sur le résultat.

Cas pratique : accès interne libre, accès externe authentifié

Imaginons que je veuille laisser passer les utilisateurs de mon réseau interne sans mot de passe, mais exiger une authentification pour tout le reste du monde. Voici comment je l'écris :

<Directory /var/www/backoffice>
    AuthType Basic
    AuthName "Back-office"
    AuthUserFile /etc/apache2/.htpasswd
    <RequireAny>
        Require ip 192.168.1.0/24
        Require valid-user
    </RequireAny>
</Directory>

Le bloc <RequireAny> dit clairement : soit vous venez de la plage interne, soit vous êtes authentifié. C'est lisible, il n'y a pas d'ambiguïté, et le comportement est celui attendu. En 2.2, on aurait obtenu le même résultat avec Satisfy any et un savant mélange d'Allow et d'Auth, avec beaucoup plus de risques d'erreur. La différence est spectaculaire.

Imbriquer les blocs sans se tromper

Quand les règles deviennent plus fines, on peut imbriquer. Par exemple, autoriser les GET depuis une IP, et exiger une authentification pour toutes les autres méthodes. Attention toutefois à ne pas créer de configurations illisibles. Mon conseil : n'imbriquez pas au-delà de deux niveaux, et commentez chaque bloc. Si une règle devient trop complexe, séparez-la en plusieurs blocs <Directory> plus ciblés. La lisibilité de la configuration est un facteur de sécurité sous-estimé. Une règle qu'on ne comprend plus est une règle qu'on finit par désactiver.

Les erreurs qui produisent un 403 incompréhensible

Le 403 est la plaie du débogage. Il se produit partout et ne dit pas pourquoi il se produit. Lorsqu'il est causé par une règle Require mal écrite, on peut perdre des heures. Voici les trois cas que je vois le plus souvent, et comment les éviter.

Cumul involontaire de clauses Require

Quand plusieurs clauses Require sont présentes au même niveau sans bloc englobant, elles sont combinées selon une logique implicite : les clauses positives fonctionnent en « ou », mais l'ajout d'une clause négative transforme tout en « et ». Cela produit des situations paradoxales, où une règle destinée à ouvrir l'accès bloque en réalité tout. La solution, c'est d'utiliser un bloc explicite, même pour deux clauses. Prenez l'habitude d'envelopper systématiquement. Vous y gagnerez en clarté et en prédictibilité.

Conflit avec mod_access_compat chargé en parallèle

Si mod_access_compat est chargé en même temps que vos règles Require, un mélange de syntaxes peut produire des comportements étranges. Certaines configurations historiques fonctionnent par chance, puis tombent après une mise à jour mineure. Le mieux est de choisir une syntaxe et de s'y tenir. Charger mod_access_compat pour une ou deux directives résiduelles, c'est entretenir une dette technique. Faites le tri dans vos fichiers et supprimez les directives anciennes dès que possible.

Héritage des blocs Directory et .htaccess

Les règles Require héritent et se combinent entre un bloc parent et un bloc enfant. Un fichier .htaccess peut ainsi ajouter une clause qui contredit celle du bloc <Directory> parent, souvent sans que l'auteur s'en rende compte. Pour diagnostiquer, vérifiez le contenu du fichier distribué en question et le bloc <Directory> correspondant. L'héritage est une bonne chose quand il est maîtrisé, mais il faut le documenter pour éviter les surprises. Un site avec cinq fichiers .htaccess imbriqués peut vite devenir une jungle.

Sécuriser des zones sensibles avec Require

La sécurité applicative commence souvent par un simple bloc Apache bien configuré. Resserrer l'accès à un back-office, à un répertoire d'administration ou à une interface d'outillage, c'est du gain de sécurité immédiat. La directive Require se prête bien à ces scénarios, avec quelques lignes ciblées.

Protéger un back-office avec une combinaison IP et mot de passe

Pour un back-office accessible depuis un réseau d'entreprise et une connexion distante, la combinaison est classique : plage interne autorisée, plus authentification pour les utilisateurs nomades. On écrit un bloc <RequireAny> avec Require ip 10.0.0.0/8 et Require valid-user. Le mot de passe doit être vérifié via AuthUserFile et AuthType Basic ou AuthType Digest. Pensez à bien protéger le fichier htpasswd en le plaçant hors du DocumentRoot, et à le restreindre par permissions Unix. C'est un détail, mais un mot de passe stocké dans un répertoire public est aussi utile qu'une porte ouverte.

Restreindre l'accès à un répertoire d'administration

Pour un répertoire d'administration qui ne doit être accessible qu'en interne, on utilise plutôt Require ip seul, avec une liste explicite des plages autorisées. On peut ajouter Require not ip pour exclure une plage spécifique, par exemple une zone wifi invitée. Le tout s'exprime dans un bloc <RequireAll> qui contient une clause positive et une clause négative. Cette structure est claire et directe. Et comme elle est lisible d'un coup d'œil, elle résiste mieux aux relectures précipitées.

Bloquer des méthodes HTTP indésirables

Le fournisseur method permet de bloquer des méthodes comme PUT, DELETE ou TRACE sur des répertoires qui ne les utilisent pas. Par exemple, un site statique n'a aucune raison d'accepter autre chose que GET, HEAD et POST. On peut écrire une règle <RequireAll> avec Require method GET HEAD POST et une clause implicite qui refuse le reste. Cette petite règle ne coûte rien et ferme une surface d'attaque. En 2026, avec les scanners automatisés qui parcourent le web, ce genre de durcissement reste utile. Il ne remplace pas un pare-feu applicatif, mais il complète bien une stratégie de défense en profondeur.

Exemples concrets de configuration commentée

Rien ne vaut un exemple complet pour voir comment tout s'articule. Les deux configurations qui suivent sont directement utilisables, à adapter évidemment.

Configuration complète d'un VirtualHost protégé

<VirtualHost *:443>
    ServerName admin.exemple.fr
    DocumentRoot /var/www/admin
    SSLEngine on
    SSLCertificateFile /etc/ssl/certs/admin.crt
    SSLCertificateKeyFile /etc/ssl/private/admin.key

    <Directory /var/www/admin>
        Options -Indexes
        AllowOverride None
        AuthType Basic
        AuthName "Administration"
        AuthUserFile /etc/apache2/.htpasswd-admin
        <RequireAll>
            Require ip 192.168.1.0/24 10.0.0.0/8
            Require valid-user
        </RequireAll>
    </Directory>

    ErrorLog ${APACHE_LOG_DIR}/admin-error.log
    CustomLog ${APACHE_LOG_DIR}/admin-access.log combined
</VirtualHost>

Ici, l'accès exige à la fois l'appartenance à une plage interne et une authentification. Le serveur ferme aussi l'indexation de répertoire et désactive les fichiers distribués. C'est une configuration typique pour une interface d'administration, et elle tient en une trentaine de lignes. Le fait que tout soit regroupé dans un même bloc rend la relecture facile. On pourrait bien sûr ajuster les plages en fonction du contexte, mais la structure reste la même.

Require dans un fichier .htaccess : ce qui change

Dans un fichier .htaccess, la syntaxe est identique, mais la portée dépend de AllowOverride. Si AllowOverride n'autorise pas AuthConfig ou Limit, les directives sont ignorées, parfois sans message clair. Pensez à vérifier ce point avant de vous arracher les cheveux sur un 403. Il faut aussi que AllowOverride autorise les directives utilisées, ce qui n'est pas le cas par défaut sur beaucoup de distributions. Et comme le fichier est relu à chaque requête, il peut ralentir le site. Réservez donc .htaccess aux cas où vous n'avez pas la main sur le fichier principal.

Tester et déboguer son contrôle d'accès

Une règle d'accès bien écrite mérite d'être testée. Malheureusement, il n'existe pas de commande magique qui affiche l'évaluation de chaque clause. Il faut donc procéder par étape, en s'appuyant sur les outils classiques d'Apache.

Vérifier avec apachectl configtest et httpd -S

apachectl configtest valide la syntaxe de la configuration. Il détecte les directives mal placées, les modules manquants et les erreurs de fichier. httpd -S liste les serveurs virtuels et leurs règles de correspondance, ce qui permet de vérifier qu'un <VirtualHost> n'en masque pas un autre. Ces deux commandes sont indispensables, mais elles ne disent rien sur la logique de vos règles. Elles vérifient la forme, pas le fond. Pour le fond, il faut tester avec un vrai client.

Diagnostiquer les 403 à l'aide des journaux

Le journal d'erreurs indique généralement la cause d'un refus : une clause Require non satisfaite, un module manquant, ou une règle héritée. Activez un niveau de log plus verbeux en développement, par exemple LogLevel alert authz_core:debug, pour voir quelles clauses sont évaluées et avec quel résultat. En production, restez sobre et contentez-vous de LogLevel warn. Un log trop bavard peut produire des fichiers énormes en quelques heures. La clé, c'est d'activer les bons logs au bon moment, pas de tout activer en permanence.

FAQ : les questions fréquentes sur Require

Peut-on encore utiliser Allow et Deny en 2.4 ? Oui, à condition de charger mod_access_compat. Mais cette solution de compatibilité est déconseillée pour de nouvelles configurations : elle vous maintient dans une syntaxe appelée à disparaître.

Quelle différence entre Require local et Require ip 127.0.0.1 ? Require local accepte les requêtes venant de la machine elle-même, ce qui inclut les connexions via IPv4 et IPv6 sur l'interface loopback, et parfois des adresses spécifiques selon la configuration réseau. Require ip 127.0.0.1 ne couvre que cette adresse précise. Pour un usage interne, Require local est généralement plus fiable.

Comment autoriser un répertoire pour tout le monde sauf une IP ? Utilisez un bloc <RequireAll> avec Require all granted et Require not ip 203.0.113.42. Le message est clair : tout le monde passe, sauf cette adresse. En 2.2, l'équivalent demandait un subtil jeu d'Order qui rendait la règle beaucoup moins évidente à lire.

Conclusion : adopter Require une fois pour toutes

La directive Require a tout d'une bonne idée mal comprise. Elle est plus claire, plus modulaire et plus expressive que l'ancien système, à condition de prendre le temps de l'apprendre. Charger mod_access_compat pour éviter la migration, c'est repousser un problème qui finira par vous rattraper, à un moment où vous aurez moins de temps pour le traiter. Adopter la nouvelle syntaxe, c'est faire un investissement modeste avec un gain durable. En 2026, avec les exigences de sécurité qui ne cessent d'augmenter, cette transition n'a plus rien d'optionnel. Alors oui, il faut parfois réécrire quelques blocs, tester, ajuster. Mais une fois passé ce cap, votre configuration devient plus lisible, votre équipe gagne en confiance, et vos utilisateurs reçoivent les bonnes réponses au lieu de tomber sur un 403 inexplicable. C'est tout ce qu'on peut souhaiter à un serveur bien tenu.

← molitor.photography