server { listen 80; root /usr/share/nginx/html/app; # Named location — отдаёт React SPA без повторной маршрутизации location @spa { add_header Cache-Control "no-store, must-revalidate"; try_files /index.html =404; } # Корневая страница — index.html никогда не кешируем, чтобы всегда грузился # актуальный билд (ассеты внутри — с хешами в имени) location = / { add_header Cache-Control "no-store, must-revalidate"; try_files /index.html =404; } # Статические ассеты приложения (JS, CSS, шрифты, картинки) — имена с хешем, # можно кешировать надолго location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot|json|map)$ { add_header Cache-Control "public, max-age=31536000, immutable"; try_files $uri =404; } # Админ-панель — React SPA, все вложенные пути разрешены location ^~ /admin { try_files $uri @spa; } # Редактор статей (отдельный React-билд) location ^~ /editor/ { root /usr/share/nginx/html; try_files $uri @editor_spa; } location @editor_spa { root /usr/share/nginx/html; try_files /editor/index.html =404; } # Статические ассеты редактора (Vite base: '/editor-assets/') location ^~ /editor-assets/ { alias /usr/share/nginx/html/editor/; } # Проксируем API-запросы на FastAPI location /api/ { proxy_pass http://api:8000/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_buffering off; } # Всё остальное — редирект на корень location / { return 302 /; } gzip on; gzip_types text/plain text/css application/json application/javascript text/xml application/xml; }