Serveur Apache HTTP Version 2.2
Le code gérant les serveurs virtuels a été réécrit à partir de
zéro dans Apache 1.3. Ce document vise à expliquer
dans le détail comment Apache procède lors du choix de l'utilisation
d'un serveur virtuel en fonction d'une requête reçue. L'apparition
de la directive NameVirtualHost
a rendu beaucoup plus facile et plus sûre la configuration des
serveurs virtuels par rapport aux versions précédant la 1.3.
Si vous voulez juste que ça marche sans en comprendre le fonctionnement, voici quelques exemples.
Un serveur principal (main_server) contient toutes
les définitions qui apparaissent en dehors des sections
<VirtualHost>
. Les serveurs virtuels, aussi
appelés vhosts (pour virtual hosts), sont définis par les
sections <VirtualHost>
.
Les directives
ServerName
et
ServerPath
peuvent être placées n'importe où dans le cadre de définition d'un
serveur. Cependant, chaque fois que l'une d'elles est lue, elle écrase
ses instances précédentes (dans le contexte du même serveur).
Le serveur principal n'a pas de valeur par
défaut pour ServerPath
ni pour ServerAlias
.
La valeur par défaut de ServerName
est déduite à partir
de l'adresses IP du serveur.
Les numéros de port spécifiés par la directive
VirtualHost
n'ont rien à voir avec les ports sur
lesquels Apache va se mettre en écoute. Ils permettent seulement
de déterminer quel VirtualHost
devra être
sélectionné pour traiter la requête.
Chaque adresse incluse dans une directive VirtualHost
peut disposer d'un port optionnel. Si le port n'est pas précisé, il
pourra prendre n'importe quelle valeur. Le port particulier
*
représente un joker qui correspond à tous les ports.
L'ensemble des adresses (y compris les résultats multiples
A
issus des requêtes DNS) est appelé jeu
d'adresses du serveur virtuel.
À moins qu'une directive
NameVirtualHost
ne soit utilisée
pour la paire adresse IP exacte/port dans la directive
VirtualHost
, Apache sélectionne le serveur virtuel qui
correspond le mieux en se basant sur l'adresse IP (ou une adresse
quelconque) et le numéro de port. Si plusieurs serveurs virtuels
correspondent sans pouvoir être départagés, c'est le premier qui
apparaît dans le fichier de configuration qui sera sélectionné.
Si vous souhaitez qu'Apache affine ses critères de
sélection en faisant entrer en jeu l'en-tête HTTP Host
fourni par le client, la directive NameVirtualHost
doit
apparaître avec la paire adresse IP exacte (ou adresse
quelconque)/port utilisée dans le jeu de directives
VirtualHost
correspondant.
La sélection du serveur virtuel en fonction du nom n'intervient qu'après la sélection d'un serveur virtuel à base d'adresse IP unique, et ne prend en compte que l'ensemble des serveurs virtuels qui possèdent la même paire adresse IP/port.
On peut utiliser des nom d'hôtes à la place d'adresses IP dans la définition des serveurs virtuels, mais ils seront résolus en adresses IP au démarrage du serveur et ceci n'est pas recommandé.
On peut utiliser plusieurs directives NameVirtualHost
pour un groupe de directives VirtualHost
, mais seule
une directive NameVirtualHost
doit être utilisée pour
chaque couple IP:port donné.
L'ordre d'apparition des directives NameVirtualHost
et VirtualHost
est sans importance, ce qui fait que
les deux exemples suivants ont des effets identiques (seul l'ordre
des directives VirtualHost
pour un jeu
d'adresses est important, voir ci-dessous) :
|
|
(Il est conseillé d'adopter le choix de gauche pour faciliter la lisibilité des fichiers de configuration.)
Pendant la phase d'initialisation, une liste de chaque adresse
IP est générée et introduite dans une table de 'hash'. Si une
adresse IP est utilisée dans une directive NameVirtualHost
,
cette liste contient les noms des serveurs virtuels pour cette
adresse. Si aucun serveur virtuel n'est défini pour cette adresse,
la directive NameVirtualHost
est ignorée et un message
est envoyé au journal d'erreurs. Quand un serveur virtuel par IP
est utilisé, la table de 'hash' reste vide.
La fonction de 'hash' étant rapide, le temps d'exécution d'un 'hash' sur une adresse IP lors d'une requête est minimale et quasiment imperceptible. De plus, la table est optimisée pour les adresses IP dont le dernier octet est le seul à changer.
Pour chaque serveur virtuel, diverses valeurs sont initialisées par défaut. En particulier :
ServerAdmin
,
Timeout
,
KeepAliveTimeout
,
KeepAlive
,
MaxKeepAliveRequests
,
ReceiveBufferSize
,
ou SendBufferSize
,
alors la valeur de chacun de ces paramètres est héritée de celle du
serveur principal. (C'est à dire, héritée de la valeur finale après
lecture de la configuration du serveur principal.)L'essentiel des valeurs de configuration des serveurs virtuels provient de valeurs par défaut issues du serveur principal. Mais la position dans le fichier de configuration des directives du serveur principal n'a pas d'importance -- l'ensemble de la configuration du serveur principal est lu avant que ces valeurs par défaut soient appliquées aux serveur virtuels. Ainsi, même si la définition d'une valeur apparaît après celle d'un serveur virtuel, cette valeur peut affecter la definition du serveur virtuel.
Dans le cas où le serveur principal n'a pas de ServerName
à ce stade, le nom de la machine sur laquelle tourne le programme
httpd
est utilisé à sa place. Nous appellerons
jeu d'adresses du serveur principal les adresses IP
renvoyées par une résolution DNS sur le ServerName
du serveur principal.
Pour tous les champs ServerName
non définis, dans
le cas d'une configuration en serveur virtuel par nom, la valeur
adoptée par défaut est la première adresse donnée dans la section
VirtualHost
qui définit le serveur virtuel.
Si un serveur virtuel contient la valeur magique
_default_
, il fonctionne sur le même ServerName
que le serveur principal.
À la réception d'une requête, le serveur procède comme suit pour déterminer quel serveur virtuel utiliser :
Après que le client se soit connecté, l'adresse IP à laquelle le client s'est connecté est recherchée dans la table de hash IP interne.
Si la résolution de l'adresse IP n'aboutit pas (adresse IP non
trouvée), la requête est servie par le serveur virtuel
_default_
s'il est défini pour le port correspondant
à la requête. Sinon, elle est servie par le serveur principal.
Si l'adresse IP n'est pas trouvée dans la table de hash, la
recherche du numéro de port peut aussi se terminer par une
correspondance à un NameVirtualHost *
qui est géré
ensuite comme les autres serveurs virtuels par noms.
Si une liste est bien trouvée dans la table pour l'adresse IP recherchée, l'étape suivante est de déterminer s'il s'agit d'un serveur virtuel par nom ou par IP.
Si l'entrée trouvée dispose d'une liste de noms vide, c'est qu'il s'agit d'un serveur virtuel par IP, et aucun autre choix n'est plus à faire ; la requête est servie par ce serveur virtuel.
Si l'entrée trouvée correspond à un serveur virtuel par nom,
la liste de noms contient au moins une structure de serveurs
virtuels. Les serveurs virtuels se présentent dans cette liste
dans le même ordre que la lecture des directives VirtualHost
dans le fichier de configuration.
Le premier serveur virtuel de cette liste (donc, le premier
serveur virtuel du fichier de configuration
attribué à l'adresse IP spécifiée)
se voit attribuer la plus grande priorité, ce
qui signifie que c'est lui qui traite les requêtes présentant un
nom de serveur invalide ou ne présentant pas de champ
Host:
dans l'en-tête.
Si un champ Host:
est transmis dans l'en-tête de
la requête, son occurrence est recherchée dans la liste et le
premier serveur virtuel qui présente un ServerName
ou un ServerAlias
correspondant est choisi pour
servir la requête. Il est possible que le champ Host:
contienne un numéro de port, mais Apache utilise toujours le
port sur lequel il a effectivement reçu la requête.
Dans le cas où le client a envoyé une requête en HTTP/1.0 sans
champ d'en-tête Host:
, il est impossible de
déterminer le serveur auquel le client veut se connecter ; l'URI
de la requête est recherché dans tous les ServerPath
existants. Le premier chemin trouvé est utilisé et la requête est
servie par le serveur virtuel correspondant.
Si aucun serveur virtuel n'est trouvé, la requête est servie par le premier serveur virtuel qui écoute sur le port demandé et qui est sur la liste associée à l'adresse IP vers laquelle la requête a été envoyée (comme déjà précisé ci-avant).
La recherche par adresse IP décrite ci-avant n'est faite qu'une fois pour chaque session TCP/IP, alors que la recherche par nom est réalisée pour chaque requête au cours d'une connexion persistante (KeepAlive). En d'autres termes, il est possible pour un client de faire des requêtes sur différents serveurs virtuels par nom, au cours d'une unique connexion persistante.
Au cas où l'URI de la requête est absolu, et que son nom de serveur et son port correspondent au serveur principal (ou l'un des serveurs virtuels configurés), et qu'ils correspondent à l'adresse et au port de la requête, alors l'URI est amputé de son préfixe protocole/nom de serveur/port et traité par le serveur correspondant (principal ou virtuel). Si cette correspondance n'existe pas, l'URI reste inchangé et la requête est considérée comme une requête d'un serveur mandataire (proxy).
NameVirtualHost
.ServerAlias
et
ServerPath
ne sont jamais réalisées pour les
serveurs virtuels par IP._default_
, les
serveurs virtuels par nom et par IP, et la directive
NameVirtualHost
est sans incidence sur le
fonctionnement. Seul l'ordre des serveurs virtuels par nom
pour une adresse donnée a une importance. Le serveur virtuel
par nom qui est présent en premier dans la configuration se
voit attribué la priorité la plus haute pour les requêtes
arrivant sur son jeu d'adresses IP.Host:
n'est jamais utilisé
pour les tests de correspondances. Apache ne prend en compte
que le numéro de port sur lequel le client a envoyé la requête.ServerPath
existe, et se
trouve être préfixe d'une autre directive ServerPath
qui apparaît plus loin dans la configuration, la première
sera toujours utilisée et la deuxième jamais. (Ceci ne se
produit que dans le cas où aucun champ Host:
n'a été présenté par le client pour distinguer les deux.)_default_
ne sert la requête
que si aucun autre serveur virtuel travaillant sur l'adresse
IP et le port demandés n'est trouvé. La requête n'est
traitée que si le numéro de port qui a reçu la requête est
associé au serveur virtuel _default_
(qui par
défaut, correspond à Listen
). Un port joker peut
être spécifié (comme dans _default_:*
)
pour récupérer les requêtes sur tous les ports ouverts. Ceci
est également applicable aux serveurs virtuels
NameVirtualHost *
. Notez que ceci n'est qu'une
extension du principe de "meilleure correspondance", au même titre
qu'une correspondance spécifique et exacte est préférée à une
valeur quelconque._default_
). En d'autres termes, le serveur
principal n'est utile que pour les combinaisons adresse/port
non spécifiées (sauf quand un serveur virtuel _default_
correspond au port)._default_
, ni le
serveur principal ne sont utilisés pour traiter une requête
avec un champ d'en-tête Host:
inconnu ou manquant
lorsque l'adresse (et le port) de connexion correspondent à
des serveurs virtuels par nom, par exemple, dans une directive
NameVirtualHost
.VirtualHost
, car cela oblige le serveur a s'appuyer
sur le DNS au moment du démarrage. De plus, vous vous exposez
à des problèmes de sécurité si vous n'avez pas la maîtrise du
DNS pour la totalité de vos domaines. Voir la documentation
disponible ici, ainsi que
les deux points précisés ci-après.ServerName
devrait toujours
être indiqué pour chaque serveur virtuel. Sans cela, une
résolution DNS est nécessaire pour chaque serveur virtuel.En plus des points évoqués sur la page des problèmes liés au DNS, voici quelques points intéressants :
VirtualHost
.
(Ceci améliore grandement la lisibilité de la configuration
-- la manière dont la configuration est interprétée après la
lecture des fichiers ne met pas en évidence le fait que les
définitions positionnées avant et surtout après les serveurs
virtuels peuvent impacter le fonctionnement de tous les
serveurs virtuels.)NameVirtualHost
et VirtualHost
correspondantes
dans la configuration pour une meilleure lisibilité.ServerPaths
qui sont préfixes
d'autres ServerPaths
. Si cela ne peut être évité,
veillez à ce que le serveur virtuel contenant le préfixe le plus
long (donc le plus précis) apparaisse dans le fichier de
configuration avant le plus court. (par exemple,
"ServerPath /abc" est à spécifier après "ServerPath /abc/def").