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 :

nginx_minio_spa.conf
    # 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 :

nginx.conf
# /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.

docker-compose.yml
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"

Check out similar posts by category: Nginx, S3, Angular