nginx : Configuration Docker pour servir une application monopage Angular publique sur S3/MinIO en HTTP
Voici ma configuration nginx qui sert automatiquement une application monopage Angular publique sur S3/MinIO en HTTP. Elle est prévue pour être utilisée dans une configuration avec un proxy externe fournissant HTTPS. Dans mon cas, il s’agit de Traefik. Voir Configuration simple docker-compose avec Traefik, Lets Encrypt et les challenges Cloudflare DNS-01, TLS-ALPN-01 et HTTP-01 pour plus de détails sur ma configuration.
Cette variante de configuration n’inclut pas la configuration pour les services @angular/localize sur S3. Voir Comment utiliser nginx pour livrer des interfaces Angular multilingues (i18n) avec @angular/localize pour un exemple nginx servant un site statique avec plusieurs langues et une logique de sélection automatique.
nginx.conf
Vous n’avez généralement besoin de modifier que les variables suivantes ici :
# Modifiez ceci avec le nom de votre bucket S3. Assurez-vous qu'il est public !
set $bucket "/my-bucket";
# Modifiez ceci avec votre hôte MinIO/S3 (sans schéma)
set $minio_host "minio.mydomain.com";Configuration complète :
# /etc/nginx/nginx.conf
worker_processes auto;
events { worker_connections 1024; }
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
# DNS via l'hôte Docker (DNS intégré à Docker)
resolver 127.0.0.11 valid=24h ipv6=off;
resolver_timeout 5s;
# gzip optionnel pour les ressources textuelles
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_proxied any;
gzip_types
text/plain text/css application/javascript application/json
application/xml application/rss+xml image/svg+xml;
# Cache disque (ajustez la taille selon vos besoins)
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=STATIC:100m max_size=10g inactive=30d use_temp_path=off;
server {
listen 80;
server_name _; # Traefik route vers ici
# Variable de commodité pour le chemin de votre bucket
# Variables de commodité pour le chemin de votre bucket et l'hôte MinIO
set $bucket "/my-bucket";
# Modifiez ceci avec votre hôte MinIO/S3 (sans schéma)
set $minio_host "minio.techoverflow.net";
# Intercepter les 404/403 en amont et router vers le SPA
error_page 404 = @spa;
error_page 403 = @spa;
# Racine exacte "/" -> servir index.html
location = / {
proxy_pass https://$minio_host$bucket/index.html;
proxy_set_header Host $minio_host;
proxy_ssl_server_name on;
proxy_buffering on;
proxy_cache STATIC;
proxy_cache_key $scheme$proxy_host$uri$is_args$args;
# Ne pas mettre en cache le HTML du SPA dans les navigateurs (optionnel)
add_header Cache-Control "no-cache, must-revalidate" always;
# Intercepter les erreurs pour que error_page fonctionne même depuis la couche de cache
proxy_intercept_errors on;
# Éviter de mettre en cache les erreurs en amont
proxy_no_cache $upstream_status = 404;
proxy_cache_bypass $upstream_status = 404;
proxy_no_cache $upstream_status = 403;
proxy_cache_bypass $upstream_status = 403;
add_header X-Cache-Status $upstream_cache_status always;
}
# Gestion unifiée pour tout le reste
location / {
proxy_pass https://$minio_host$bucket$uri$is_args$args;
proxy_set_header Host $minio_host;
proxy_ssl_server_name on;
proxy_buffering on;
proxy_http_version 1.1;
proxy_cache STATIC;
proxy_cache_key $scheme$proxy_host$uri$is_args$args;
# TTLs unifiés pour les réponses réussies
proxy_cache_valid 200 301 302 24h;
# Ne pas mettre en cache les erreurs en amont (évite de servir un 404 NGINX brut)
proxy_no_cache $upstream_status = 404;
proxy_cache_bypass $upstream_status = 404;
proxy_no_cache $upstream_status = 403;
proxy_cache_bypass $upstream_status = 403;
# Mise en cache navigateur optionnelle (activez si vous versionnez vos ressources)
# add_header Cache-Control "public, max-age=86400, immutable" always;
add_header X-Cache-Status $upstream_cache_status always;
# S'assurer que le repli SPA se déclenche sur les 404/403 depuis l'origine
proxy_intercept_errors on;
}
# Cible du repli SPA
location @spa {
proxy_pass https://$minio_host$bucket/index.html;
proxy_set_header Host $minio_host;
proxy_ssl_server_name on;
proxy_buffering on;
proxy_cache STATIC;
proxy_cache_key $scheme$proxy_host$uri$is_args$args;
add_header Cache-Control "no-cache, must-revalidate" always;
add_header X-Cache-Status $upstream_cache_status always;
}
}
}docker-compose.yml
La configuration docker-compose est assez simple.
services:
nginx:
image: nginx:alpine
restart: unless-stopped
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx_cache:/var/cache/nginx
labels:
- "traefik.enable=true"
- "traefik.http.routers.app-mydomain.rule=Host(`app.mydomain.com`)"
- "traefik.http.routers.app-mydomain.entrypoints=websecure"
- "traefik.http.routers.app-mydomain.tls.certresolver=cloudflare"
- "traefik.http.routers.app-mydomain.tls.domains[0].main=mydomain.com"
- "traefik.http.routers.app-mydomain.tls.domains[0].sans=*.mydomain.com"