Apache : lorsque le domaine seul (sans WWW) renvoie une erreur 403

Après avoir ajouté un sous-domaine dédié aux images, j’ai remarqué qu’en appelant le domaine nu, par exemple skyminds.net sans www, je tombais sur une erreur 403 Forbidden.

Pourtant, jusque-là, le domaine sans www avait toujours été redirigé vers www.skyminds.net.

En analysant les logs Apache, je me suis rendu compte que le domaine nu ne tombait plus sur le bon virtual host. Apache servait le contenu d’un sous-domaine dédié aux ressources statiques. Or ce sous-domaine ne contenait que des images et n’était pas prévu pour afficher une page d’accueil. Résultat : erreur 403.

Le problème venait tout simplement d’une configuration incomplète du virtual host principal.

Comprendre le problème : Apache choisit un virtual host

Sur un serveur Apache qui héberge plusieurs domaines ou sous-domaines sur la même adresse IP, Apache utilise les virtual hosts pour décider quel site doit répondre à une requête.

Le navigateur envoie un header Host, par exemple :

Host: skyminds.netCode language: CSS (css)

Apache cherche alors un virtual host dont le ServerName ou le ServerAlias correspond à ce nom.

Si aucun virtual host ne correspond, Apache utilise le premier virtual host chargé pour cette adresse IP et ce port. La documentation Apache explique justement que si aucune correspondance plus spécifique n’est trouvée, le premier virtual host listé devient le serveur par défaut pour cette paire IP/port.

C’est là que les choses deviennent amusantes, au sens “Apache vient de servir le mauvais site avec beaucoup de sérieux”.

Symptôme classique

Le site en www fonctionne :

https://www.example.com/Code language: JavaScript (javascript)

Mais le domaine nu renvoie une erreur 403 :

https://example.com/Code language: JavaScript (javascript)

Ou bien il affiche le mauvais site, le mauvais sous-domaine, une page par défaut Apache, ou une erreur de certificat SSL.

Dans mon cas, skyminds.net tombait sur le virtual host du sous-domaine statique. Comme le répertoire n’était pas prévu pour être listé ou servir une page index, Apache répondait logiquement avec une 403.

La correction simple : ajouter le domaine nu dans ServerAlias

La correction consiste à déclarer le domaine nu dans le virtual host principal.

Exemple si votre domaine canonique est www.example.com :

<VirtualHost *:80>
    ServerName www.example.com
    ServerAlias example.com

    DocumentRoot /home/www/example.com/public_html
</VirtualHost>Code language: HTML, XML (xml)

Et pour HTTPS :

<VirtualHost *:443>
    ServerName www.example.com
    ServerAlias example.com

    DocumentRoot /home/www/example.com/public_html

    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/www.example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/www.example.com/privkey.pem
</VirtualHost>Code language: HTML, XML (xml)

La directive ServerName définit le nom principal du virtual host. ServerAlias permet d’ajouter les autres noms qui doivent pointer vers le même virtual host, comme le domaine nu, le www, ou d’autres alias.

Dans mon cas, la correction minimale était donc :

ServerName www.skyminds.net
ServerAlias skyminds.netCode language: CSS (css)

Une fois cette ligne ajoutée dans le bon virtual host, le domaine sans www retombe sur le bon site.

Mais faut-il servir les deux versions ou rediriger ?

Techniquement, ServerAlias permet de faire répondre le même virtual host à plusieurs noms.

Mais côté SEO et maintenance, je préfère choisir une version canonique :

  • https://www.example.com/ ;
  • ou https://example.com/.

Ensuite, l’autre version doit rediriger en 301 vers la version canonique. Cela évite les doublons, les cookies dispersés, les URLs incohérentes, et les petites surprises côté cache.

Apache documente plusieurs méthodes de redirection et de remapping avec mod_rewrite, mais pour une redirection simple de host, un virtual host dédié avec Redirect permanent est souvent plus lisible.

Solution propre : rediriger le domaine nu vers www

Si vous voulez que example.com redirige toujours vers www.example.com, créez un virtual host dédié au domaine nu.

HTTP :

<VirtualHost *:80>
    ServerName example.com
    Redirect permanent / https://www.example.com/
</VirtualHost>Code language: HTML, XML (xml)

HTTPS :

<VirtualHost *:443>
    ServerName example.com

    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem

    Redirect permanent / https://www.example.com/
</VirtualHost>Code language: HTML, XML (xml)

Puis le virtual host principal :

<VirtualHost *:443>
    ServerName www.example.com

    DocumentRoot /home/www/example.com/public_html

    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/www.example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/www.example.com/privkey.pem

    <Directory /home/www/example.com/public_html>
        Options FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>Code language: HTML, XML (xml)

Cette approche est très claire : le domaine nu ne sert aucun contenu. Il redirige immédiatement vers la version canonique en www.

Solution inverse : rediriger www vers le domaine nu

Si vous préférez la version sans www, inversez la logique.

Virtual host de redirection :

<VirtualHost *:80>
    ServerName www.example.com
    Redirect permanent / https://example.com/
</VirtualHost>

<VirtualHost *:443>
    ServerName www.example.com

    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/www.example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/www.example.com/privkey.pem

    Redirect permanent / https://example.com/
</VirtualHost>Code language: HTML, XML (xml)

Virtual host principal :

<VirtualHost *:443>
    ServerName example.com

    DocumentRoot /home/www/example.com/public_html

    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem

    <Directory /home/www/example.com/public_html>
        Options FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>Code language: HTML, XML (xml)

Le choix www ou sans www est surtout une préférence d’architecture et d’historique SEO. L’important est de choisir une version, puis de s’y tenir.

Distingo, le livret à 2%

Diagnostic avec apachectl -S

La commande la plus utile dans ce genre de cas est :

sudo apachectl -S

Elle affiche les virtual hosts chargés, leurs ports, leurs noms, leurs alias et les fichiers de configuration correspondants.

Exemple de sortie utile :

VirtualHost configuration:
*:80 is a NameVirtualHost
         default server static.example.com (/etc/apache2/sites-enabled/000-static.conf:1)
         port 80 namevhost static.example.com (/etc/apache2/sites-enabled/000-static.conf:1)
         port 80 namevhost www.example.com (/etc/apache2/sites-enabled/example.com.conf:1)
                 alias example.comCode language: JavaScript (javascript)

Si example.com n’apparaît pas comme alias du bon virtual host, Apache ne peut pas deviner. Il choisira le vhost par défaut, souvent le premier chargé pour le port concerné.

La documentation Apache conseille d’ailleurs de déclarer explicitement un ServerName dans chaque virtual host nommé, car ce nom influence la résolution des virtual hosts.

Vérifier les fichiers activés dans sites-enabled

Sur Debian/Ubuntu, les fichiers activés se trouvent généralement dans :

/etc/apache2/sites-enabled/

Listez-les dans l’ordre :

ls -l /etc/apache2/sites-enabled/

Apache charge les fichiers selon leur ordre de nom. Un fichier qui commence par 000- peut devenir le virtual host par défaut pour un port donné.

Ce n’est pas forcément un problème, mais il faut que ce soit volontaire. Un sous-domaine statique ne devrait généralement pas être le vhost par défaut si vous ne voulez pas qu’un domaine mal déclaré tombe dessus.

Créer un virtual host catch-all propre

Pour éviter qu’un domaine non déclaré tombe sur un site au hasard, on peut créer un vhost par défaut volontaire.

Par exemple :

<VirtualHost *:80>
    ServerName default.invalid
    Redirect 404 /
</VirtualHost>Code language: HTML, XML (xml)

Ou, plus explicitement :

<VirtualHost *:80>
    ServerName default.invalid
    DocumentRoot /var/www/empty

    <Directory /var/www/empty>
        Require all denied
    </Directory>
</VirtualHost>Code language: PHP (php)

Apache 2.4 utilise la directive Require pour autoriser ou refuser l’accès. Par exemple, Require all granted autorise l’accès, tandis que Require all denied le refuse.

Un vhost catch-all évite qu’un domaine oublié expose un autre site par erreur. C’est souvent plus propre sur un serveur multi-sites.

Vérifier les logs Apache

Pour comprendre pourquoi Apache renvoie une 403, commencez par les logs :

sudo tail -f /var/log/apache2/error.logCode language: JavaScript (javascript)

Ou les logs dédiés du site :

sudo tail -f /var/log/apache2/example.com-error.logCode language: JavaScript (javascript)

Les messages typiques incluent :

client denied by server configuration
Directory index forbidden by Options directive
AH01630: client denied by server configuration

Ces messages indiquent si le problème vient d’un mauvais virtual host, d’une directive Require, d’un dossier sans index, ou d’une option Apache comme Indexes.

Vérifier avec curl quel vhost répond

Pour tester proprement les réponses HTTP :

curl -I http://example.com/
curl -I http://www.example.com/
curl -I https://example.com/
curl -I https://www.example.com/Code language: JavaScript (javascript)

Pour forcer un header Host vers une IP donnée :

curl -I -H "Host: example.com" http://203.0.113.10/
curl -I -H "Host: www.example.com" http://203.0.113.10/Code language: JavaScript (javascript)

C’est pratique si DNS n’est pas encore propagé ou si vous voulez tester directement le serveur.

Pour HTTPS avec SNI, utilisez plutôt --resolve :

curl -I --resolve example.com:443:203.0.113.10 https://example.com/
curl -I --resolve www.example.com:443:203.0.113.10 https://www.example.com/Code language: JavaScript (javascript)

Vous verrez rapidement si le domaine nu renvoie une 301, une 403, une 200, ou un certificat inattendu.

Attention au certificat HTTPS

Si le domaine nu doit rediriger en HTTPS vers www, il lui faut tout de même un certificat valide. Sinon, le navigateur affichera une erreur TLS avant même de recevoir la redirection HTTP.

Un certificat Let’s Encrypt doit donc couvrir les deux noms si les deux sont appelés en HTTPS :

example.com
www.example.comCode language: CSS (css)

Avec Certbot, cela ressemble souvent à :

sudo certbot --apache -d example.com -d www.example.comCode language: CSS (css)

Ou avec un autre client ACME, l’idée reste la même : le certificat doit inclure tous les noms qui recevront une connexion TLS directe.

Cas WordPress : régler aussi l’URL du site

Si le site est sous WordPress, Apache ne fait pas tout. WordPress possède aussi ses propres URLs dans la base :

home
siteurl

Avec WP-CLI :

wp option get home
wp option get siteurlCode language: JavaScript (javascript)

Pour forcer la version canonique en www :

wp option update home 'https://www.example.com'
wp option update siteurl 'https://www.example.com'Code language: JavaScript (javascript)

Si WordPress pense être sur example.com alors qu’Apache redirige vers www.example.com, vous pouvez créer des boucles, des cookies incohérents, ou des redirections pénibles à diagnostiquer.

Choisissez une URL canonique, configurez Apache, le certificat, WordPress et le cache dans le même sens. L’alignement, ce n’est pas que pour les chakras.

Exemple complet : domaine nu vers www en HTTPS

Voici une configuration complète et lisible pour un site canonique en www.

Redirection HTTP vers HTTPS www :

<VirtualHost *:80>
    ServerName example.com
    ServerAlias www.example.com

    Redirect permanent / https://www.example.com/
</VirtualHost>Code language: HTML, XML (xml)

Redirection HTTPS du domaine nu vers HTTPS www :

<VirtualHost *:443>
    ServerName example.com

    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem

    Redirect permanent / https://www.example.com/
</VirtualHost>Code language: HTML, XML (xml)

Site principal en HTTPS www :

<VirtualHost *:443>
    ServerName www.example.com

    DocumentRoot /home/www/example.com/public_html

    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/www.example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/www.example.com/privkey.pem

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

    <Directory /home/www/example.com/public_html>
        Options FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>Code language: HTML, XML (xml)

Selon votre méthode de génération de certificat, vous pouvez avoir un seul certificat couvrant example.com et www.example.com, ou deux certificats séparés. Le plus important est que chaque vhost HTTPS présente un certificat valide pour son ServerName.

Tester et recharger Apache

Après modification, vérifiez la syntaxe :

sudo apachectl configtest

ou :

sudo apache2ctl -t

Vérifiez aussi le mapping des virtual hosts :

sudo apachectl -S

Puis rechargez Apache :

sudo systemctl reload apache2

Ou, sur un ancien système :

sudo service apache2 reload

Un reload suffit généralement. Pas besoin de redémarrer brutalement Apache pour une modification de virtual host, sauf cas particulier.

Checklist de diagnostic

Si le domaine sans www renvoie une 403, je vérifierais dans cet ordre :

  1. Le DNS de example.com pointe-t-il vers le bon serveur ?
  2. Apache possède-t-il un vhost pour example.com ?
  3. example.com est-il dans ServerName ou ServerAlias ?
  4. apachectl -S montre-t-il le bon mapping ?
  5. Le vhost HTTPS possède-t-il un certificat valide pour example.com ?
  6. Le domaine nu doit-il servir le site ou rediriger vers www ?
  7. Le vhost par défaut est-il volontaire ?
  8. Les logs Apache indiquent-ils un problème de permissions ou de directive Require ?
  9. WordPress utilise-t-il la même URL canonique que le serveur ?

Mémo rapide

# Voir les virtual hosts chargés.
sudo apachectl -S

# Tester la configuration.
sudo apachectl configtest

# Suivre les erreurs Apache.
sudo tail -f /var/log/apache2/error.log

# Tester les réponses HTTP.
curl -I http://example.com/
curl -I http://www.example.com/
curl -I https://example.com/
curl -I https://www.example.com/

# Tester HTTPS vers une IP précise.
curl -I --resolve example.com:443:203.0.113.10 https://example.com/

# Correction minimale dans le vhost principal.
ServerName www.example.com
ServerAlias example.com

# Redirection domaine nu vers www.
<VirtualHost *:80>
    ServerName example.com
    Redirect permanent / https://www.example.com/
</VirtualHost>

# Recharger Apache.
sudo systemctl reload apache2Code language: PHP (php)

Conclusion

Si le domaine seul, sans www, renvoie une erreur 403 alors que la version www fonctionne, Apache sert probablement le mauvais virtual host ou tombe sur un vhost par défaut.

La correction minimale consiste à ajouter le domaine nu dans le ServerAlias du virtual host principal :

ServerName www.example.com
ServerAlias example.comCode language: CSS (css)

Mais la meilleure solution consiste souvent à définir clairement une version canonique, puis à rediriger l’autre en 301.

Enfin, pensez à vérifier HTTPS, le certificat, apachectl -S, les logs et les URLs WordPress si le site tourne sous WordPress. Une erreur 403 sur le domaine nu n’est pas toujours un problème de permissions : parfois, Apache répond simplement avec le mauvais site, mais avec une confiance absolument admirable.

Des obstacles techniques ? Je trouve des solutions sur-mesure pour que votre site WordPress/WooCommerce fonctionne sans accroc.

Contactez-moi pour un diagnostic gratuit »

Gravatar for Matt Biscay

Développeur certifié WordPress & WooCommerce chez Codeable, administrateur système et enseignant-chercheur, je mets mon expertise au service de vos projets web.

Ma priorité : des sites performants, fiables et sécurisés, pensés pour offrir la meilleure expérience utilisateur. J’accompagne chaque client avec écoute et pédagogie, pour transformer vos idées en solutions concrètes et durables.

Profitez de solutions WordPress et WooCommerce sur-mesure, pensées pour optimiser durablement votre site.
Explorez les leviers pour booster l’impact de votre site web.

Opinions