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.
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 :
- Le DNS de
example.compointe-t-il vers le bon serveur ? - Apache possède-t-il un vhost pour
example.com? example.comest-il dansServerNameouServerAlias?apachectl -Smontre-t-il le bon mapping ?- Le vhost HTTPS possède-t-il un certificat valide pour
example.com? - Le domaine nu doit-il servir le site ou rediriger vers
www? - Le vhost par défaut est-il volontaire ?
- Les logs Apache indiquent-ils un problème de permissions ou de directive
Require? - 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.
