-
Objectifs
- Déposer la version compilée sur un hébergement mutualisé (FTP, gestionnaire de fichiers) ou sur un serveur dédié (
scp, rsync)
- Comprendre pourquoi une page comme
/cours/2 renvoie une erreur 404 quand on l'actualise
- Configurer Apache avec un fichier
.htaccess et Nginx avec try_files
- Installer l'application dans un sous-dossier avec
--base-href
- Régler le cache:
index.html toujours revérifié, fichiers avec empreinte gardés un an
- Mettre à jour une application déjà en ligne
-
Présentation
- La leçon précédente a produit le dossier
dist/premiers-pas-angular/browser/: quelques fichiers statiques. Les publier, c'est les copier dans le dossier qu'un serveur web expose sur Internet.
- Deux situations courantes : un hébergement mutualisé (Apache le plus souvent, on dépose les fichiers dans
public_html/ ou www/ et on ne règle le serveur qu'avec un fichier .htaccess) et un serveur dédié ou un VPS (on administre soi-même Apache ou Nginx).
- Dans les deux cas, une seule vraie difficulté : le serveur doit renvoyer
index.html pour les adresses gérées par le routeur Angular. Sans ce réglage, l'accueil fonctionne, mais toute autre page actualisée affiche une erreur 404.
- Pour suivre ce chapitre, continuez dans le projet
premiers-pas-angular, avec ses trois pages /, /cours et /cours/:id. Toutes les configurations ont été testées avec Angular 22.2, Apache 2.4 et Nginx, dans un vrai navigateur.

-
Déposer les fichiers
- Compilez d'abord en production :
ng build
- Copiez ensuite le contenu du dossier
browser/ (et non le dossier lui-même) à la racine du site. Sur la plupart des hébergements, index.html doit se trouver directement dans public_html/:
public_html/
├── .htaccess ← à créer (voir plus bas)
├── index.html
├── main-7W7XMOG5.js
├── styles-5INURTSO.css
└── favicon.ico
- Hébergement mutualisé: utilisez un client FTP (FileZilla), ou le gestionnaire de fichiers du panneau d'administration (cPanel, Plesk, aaPanel…) en déposant une archive
.zip que vous décompressez sur place.
- Serveur dédié: copiez en ligne de commande.
rsync n'envoie que les fichiers modifiés, et --delete retire du serveur les anciens fichiers avec empreinte devenus inutiles. Notez la barre oblique finale après browser/: elle copie le contenu du dossier, pas le dossier.
# envoi du contenu de browser/ dans /var/www/premiers-pas-angular/
rsync -av --delete dist/premiers-pas-angular/browser/ utilisateur@mon-serveur:/var/www/premiers-pas-angular/
# sans rsync
scp -r dist/premiers-pas-angular/browser/* utilisateur@mon-serveur:/var/www/premiers-pas-angular/
-
Le problème de l'actualisation : l'erreur 404
- Déposez les fichiers sur un serveur Apache sans aucun réglage, puis testez :
| Action |
Résultat |
Ouvrir https://mon-site.tn/ |
200 : l'accueil s'affiche |
| Cliquer sur « Cours », puis sur « Angular » |
Les pages s'affichent, l'adresse devient /cours/2 |
Actualiser (F5) sur /cours/2, ou ouvrir ce lien dans un nouvel onglet |
404 Not Found |

- Quand on clique sur un lien, c'est le routeur Angular, dans le navigateur, qui change l'adresse et la page : aucune requête n'est envoyée au serveur.
- Quand on actualise, le navigateur demande au serveur le fichier
/cours/2. Ce fichier n'existe pas sur le disque : le serveur répond 404, et Angular n'est jamais chargé.
- La solution : configurer le serveur pour qu'il envoie le fichier demandé s'il existe (
main-….js, une image…), et index.html dans tous les autres cas. Angular démarre alors, lit l'adresse /cours/2 et affiche la bonne page.

-
Apache : le fichier .htaccess
- Créez un fichier nommé exactement
.htaccess (avec le point) à côté de index.html:
RewriteEngine On
RewriteBase /
# 1. Un fichier ou un dossier qui existe : on le sert tel quel
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
# 2. Toute autre adresse (/cours, /cours/2...) : index.html, le routeur Angular fait le reste
RewriteRule ^ index.html [L]
# 3. Cache : index.html toujours revérifié, fichiers avec empreinte gardés un an
<IfModule mod_headers.c>
<FilesMatch "^index\.html$">
Header set Cache-Control "no-cache"
</FilesMatch>
<FilesMatch "-[A-Z0-9]{8}\.(js|css)$">
Header set Cache-Control "public, max-age=31536000, immutable"
</FilesMatch>
</IfModule>
- Résultat mesuré sur le serveur de test :
/ -> 200 Cache-Control: no-cache
/cours/2 -> 200 Cache-Control: no-cache
/main-7W7XMOG5.js -> 200 Cache-Control: public, max-age=31536000, immutable
/styles-5INURTSO.css -> 200 Cache-Control: public, max-age=31536000, immutable
- Actualisez
/cours/2: la page « Angular » s'affiche.
- Le fichier ne fonctionne que si le module
mod_rewrite est actif et si Apache autorise les .htaccess (AllowOverride All). C'est le cas chez presque tous les hébergeurs. Sur votre propre serveur Apache (Debian, Ubuntu), activez-les ainsi :
sudo a2enmod rewrite headers
sudo systemctl reload apache2
# /etc/apache2/sites-available/premiers-pas-angular.conf
<VirtualHost *:80>
ServerName mon-site.tn
DocumentRoot /var/www/premiers-pas-angular
<Directory /var/www/premiers-pas-angular>
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
- Deux pannes vérifiées sur le serveur de test : avec
AllowOverride None, le fichier est ignoré et /cours/2 reste en 404 ; sans mod_rewrite, tout le site répond 500 Internal Server Error, et le journal d'erreurs indique Invalid command 'RewriteEngine', perhaps misspelled or defined by a module not included in the server configuration.
- Le
.htaccess se trouve à la racine du site, pas dans dist/: chaque ng build vide ce dossier. Pour qu'il soit recopié à chaque compilation, rangez-le dans le dossier public/ du projet.
-
Installer l'application dans un sous-dossier
- Pour publier l'application à l'adresse
https://mon-site.tn/formation/, compilez avec la bonne adresse de base :
ng build --base-href /formation/
- Déposez les fichiers dans
public_html/formation/, avec ce .htaccess, où seul RewriteBase change :
RewriteEngine On
RewriteBase /formation/
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
RewriteRule ^ index.html [L]
- Vérifié dans le navigateur :
/formation/ affiche l'accueil, et /formation/cours/3 actualisé affiche le cours « Signals ».
- Si l'on oublie
--base-href, la page reste blanche: index.html demande /main-….js à la racine du site au lieu de /formation/main-….js, et la console affiche deux erreurs Failed to load resource: the server responded with a status of 404 (Not Found).

-
Nginx sur un serveur dédié
- Nginx ne lit pas les
.htaccess: on règle tout dans la configuration du site. La directive try_files joue le rôle des règles de réécriture : elle essaie le fichier, puis le dossier, puis renvoie index.html.
# /etc/nginx/sites-available/premiers-pas-angular
server {
listen 80;
server_name mon-site.tn;
root /var/www/premiers-pas-angular;
index index.html;
# Fichiers avec empreinte (main-XXXX.js) : mis en cache un an
location ~* \.(?:js|css|ico|png|jpg|svg|woff2?)$ {
expires 1y;
add_header Cache-Control "public, immutable";
try_files $uri =404;
}
# Toutes les autres adresses : le fichier s'il existe, sinon index.html
location / {
add_header Cache-Control "no-cache";
try_files $uri $uri/ /index.html;
}
# Une seconde application dans un sous-dossier (compilée avec --base-href /formation/)
location ^~ /formation/ {
alias /var/www/formation/;
try_files $uri $uri/ /formation/index.html;
}
}
- Activez le site, vérifiez la syntaxe, puis rechargez Nginx :
sudo ln -s /etc/nginx/sites-available/premiers-pas-angular /etc/nginx/sites-enabled/
sudo nginx -t
nginx: configuration file /etc/nginx/nginx.conf test is successful
sudo systemctl reload nginx
- Résultats mesurés :
/cours/2 répond 200 et affiche « Angular », /fichier-absent.js répond une vraie 404, et /formation/cours/1 affiche « TypeScript ».
- Le
^~ devant /formation/ est indispensable. Sans lui, la règle des .js intercepte /formation/main-….js et cherche le fichier dans /var/www/premiers-pas-angular: la page du sous-dossier reste blanche. Avec ^~, Nginx s'arrête à ce bloc sans tester les expressions régulières.

-
Passer en HTTPS
- Sur un hébergement mutualisé, le certificat se demande dans le panneau d'administration (rubrique SSL, souvent Let's Encrypt gratuit), puis on force le HTTPS.
- Sur un serveur dédié, l'outil
certbot obtient un certificat Let's Encrypt et modifie la configuration du site :
sudo apt install certbot python3-certbot-nginx # ou python3-certbot-apache
sudo certbot --nginx -d mon-site.tn # ou --apache
- Si l'application appelle une API, celle-ci doit aussi être en HTTPS : un site HTTPS ne peut pas appeler une adresse
http:// (le navigateur bloque la requête, contenu mixte).
-
Mettre à jour une application en ligne
- À chaque nouvelle version :
ng build, puis envoi des fichiers. Grâce aux empreintes, les nouveaux main-….js ne remplacent pas les anciens, ils s'ajoutent à côté ; seul index.html est écrasé et pointe vers les nouveaux.
- Avec
rsync --delete, les anciens fichiers sont supprimés. Par FTP, supprimez-les à la main de temps en temps (en gardant .htaccess).
- Comme
index.html est en no-cache, les visiteurs reçoivent la nouvelle version dès leur prochain chargement, sans vider leur cache.

-
Activité 1 : reproduire l'erreur 404
- Compiler le projet et déposer le contenu de
browser/ sur un serveur Apache (hébergement, XAMPP, WampServer…) sans .htaccess.
- Naviguer jusqu'à
/cours/2 en cliquant, puis actualiser la page.
- Vérifier : la navigation par clics fonctionne, l'actualisation donne une erreur 404.
- Expliquez en deux phrases pourquoi le clic fonctionne et l'actualisation non.
-
Activité 2 : corriger avec un .htaccess
- Ajouter le fichier
.htaccess de la leçon, actualiser /cours/2, puis ouvrir directement /cours/3 dans un nouvel onglet.
- Ranger ensuite le
.htaccess dans le dossier public/ du projet, recompiler, et vérifier qu'il se trouve dans dist/premiers-pas-angular/browser/.
- Vérifier : les deux pages s'affichent ; le fichier est recopié à chaque compilation.
- Pourquoi les deux lignes
RewriteCond sont-elles indispensables ? Que se passerait-il pour main-….js sans elles ?
-
Activité 3 : une application dans un sous-dossier
- Publier l'application à l'adresse
/formation/ du serveur, d'abord sans --base-href, puis avec --base-href /formation/ et le .htaccess adapté.
- Dans les deux cas, ouvrir les outils de développement (F12), onglet Réseau, et relever l'adresse demandée pour
main-….js.
- Vérifier : page blanche et erreurs 404 dans le premier cas ; application complète et
/formation/cours/3 actualisable dans le second.
- Quelles sont les deux modifications nécessaires pour passer de la racine à un sous-dossier ?
-
Activité 4 : contrôler le cache
- Dans l'onglet Réseau, cliquer sur
index.html puis sur main-….js, et relever l'en-tête Cache-Control de chacun.
- Modifier un texte de l'application, recompiler, redéployer, puis recharger la page normalement (sans vider le cache).
- Vérifier :
no-cache pour index.html, max-age=31536000 pour main-….js; la modification apparaît immédiatement.
- Que verraient les visiteurs si
index.html était lui aussi gardé un an en cache ?
-
- Sur un serveur Linux (ou une machine virtuelle), installer Nginx, copier l'application dans
/var/www/premiers-pas-angular et écrire la configuration du site avec try_files.
- Ajouter une seconde application dans
/formation/, d'abord avec location /formation/, puis avec location ^~ /formation/.
- Vérifier :
sudo nginx -t ne signale aucune erreur ; /cours/2 actualisé s'affiche ; le sous-dossier ne fonctionne qu'avec ^~.
- Pourquoi un fichier
.js absent doit-il recevoir une vraie 404 plutôt que index.html?
-
Erreurs fréquentes
| Message ou symptôme |
Cause |
Solution |
404 Not Found en actualisant /cours/2 |
Le serveur cherche un fichier /cours/2 |
.htaccess (Apache) ou try_files … /index.html (Nginx) |
Toujours 404 malgré le .htaccess |
AllowOverride None, ou fichier mal nommé (htaccess.txt) |
AllowOverride All; nommer le fichier .htaccess |
500 Internal Server Error, Invalid command 'RewriteEngine' dans le journal |
mod_rewrite inactif |
sudo a2enmod rewrite puis recharger Apache |
Page blanche, main-….js en 404 dans la console |
Sous-dossier sans --base-href |
ng build --base-href /dossier/ et RewriteBase /dossier/ |
| Page blanche dans un sous-dossier sous Nginx |
La règle des .js passe avant location /formation/ |
location ^~ /formation/ |
Liste des fichiers, page 403, ou index.html introuvable |
Dossier browser/ copié au lieu de son contenu |
Placer index.html directement à la racine du site |
| Les visiteurs voient l'ancienne version |
index.html gardé en cache |
Cache-Control: no-cache sur index.html |
Le .htaccess a disparu après une compilation |
Rangé dans dist/, vidé par ng build |
Le ranger dans public/ |
Requêtes vers l'API bloquées, Mixed Content dans la console |
Site en HTTPS, API en HTTP |
Passer l'API en HTTPS |
-
À retenir
- On publie le contenu de
dist/<projet>/browser/: index.html doit être à la racine du site (ou du sous-dossier).
- Le routeur Angular fonctionne dans le navigateur : le serveur doit renvoyer
index.html pour toute adresse qui n'est pas un fichier.
- Apache : un
.htaccess avec mod_rewrite et AllowOverride All; Nginx : try_files $uri $uri/ /index.html;.
- Sous-dossier :
ng build --base-href /dossier/, plus RewriteBase /dossier/ ou location ^~ /dossier/.
index.html en no-cache, fichiers avec empreinte en cache un an : les mises à jour arrivent tout de suite.
- Rangez le
.htaccess dans public/ pour qu'il soit recopié à chaque compilation.
Une application Angular n'est qu'un fichier index.html et ses scripts : le seul travail du serveur est de toujours répondre index.html, et de laisser le routeur faire le reste.