通过 HTTP 提供 S3/MinIO 公共 Angular 单页应用的 nginx Docker 配置

这是我的 nginx 配置,可自动通过 HTTP 提供公共 S3/MinIO Angular 单页应用。它预期与提供 HTTPS 的外部代理配合使用。在我的场景中,这个代理是 Traefik。有关我的配置详情,请参阅使用 Let’s Encrypt Cloudflare DNS-01、TLS-ALPN-01 和 HTTP-01 挑战的简单 Traefik docker-compose 设置

此配置变体不包含通过 S3 提供 @angular/localize 多语言配置的内容。有关提供多语言静态站点并带有自动选择逻辑的 nginx 示例,请参阅如何使用 nginx 提供 @angular/localize 多语言国际化 Angular UI

nginx.conf

你通常只需要更改以下变量:

nginx_minio_spa.conf
    # 将此更改为你 S3 存储桶的名称。确保它已设置为公开!
    set $bucket "/my-bucket";
    # 将此更改为你 MinIO/S3 的主机(不含协议)
    set $minio_host "minio.mydomain.com";

完整配置:

nginx.conf
# /etc/nginx/nginx.conf
worker_processes auto;

events { worker_connections 1024; }

http {
  include       mime.types;
  default_type  application/octet-stream;
  sendfile      on;

  # 通过 Docker 主机进行 DNS 解析(Docker 内嵌 DNS)
  resolver 127.0.0.11 valid=24h ipv6=off;
  resolver_timeout 5s;

  # 对文本资源可选的 gzip 压缩
  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;

  # 磁盘缓存(根据需要调整大小)
  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 路由到这里

    # 存储桶路径的便捷变量
    # 存储桶路径和 MinIO 主机的便捷变量
    set $bucket "/my-bucket";
    # 将此更改为你 MinIO/S3 的主机(不含协议)
    set $minio_host "minio.techoverflow.net";

    # 拦截上游的 404/403 并路由到 SPA
    error_page 404 = @spa;
    error_page 403 = @spa;

    # 精确匹配根路径 "/" -> 提供 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;

      # 不在浏览器中缓存 SPA HTML(可选)
      add_header Cache-Control "no-cache, must-revalidate" always;

      # 拦截错误,使 error_page 即使在缓存层也能生效
      proxy_intercept_errors on;

      # 避免缓存上游错误
      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;
    }

    # 其他所有请求的统一处理
    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;

      # 成功响应的统一 TTL
      proxy_cache_valid 200 301 302 24h;
      # 不缓存上游错误(防止提供纯 NGINX 404)
      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 Cache-Control "public, max-age=86400, immutable" always;

      add_header X-Cache-Status $upstream_cache_status always;

      # 确保在源返回 404/403 时触发 SPA 回退
      proxy_intercept_errors on;
    }

    # 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

docker-compose 配置非常简单。

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