Современный деплой статики: Полный гайд по связке Vite, Docker и Caddy Vadim Romanikov Сборка фронтенда — ещё полдела. Настоящая головная боль начинается, когда статику нужно вывести в продакшен: вручную крутить Nginx, мучиться с Certbot, следить за сертификатами. Vadim Romanikov предлагает иной путь. Vite собирает чистую статику без Node.js на сервере. Docker изолирует окружение и отсекает лишнее через многоступенчатую сборку. Caddy сам договаривается с Let's Encrypt, сам продлевает HTTPS, сам перенаправляет с HTTP — всё по одному домену в конфиге. Автор шаг за шагом выстраивает архитектуру: лаконичный Caddyfile с заголовками безопасности, Dockerfile из двух этапов, финальная оркестрация в Compose. Результат — легковесный, самообновляемый контейнер, который отдаёт сайт и не требует ночных дежурств. ==================== Современный деплой статики: Полный гайд по связке Vite, Docker и Caddy В современной веб-разработке статические сайты (SSG) и одностраничные приложения (SPA) стали золотым стандартом благодаря своей производительности и безопасности. Однако процесс их развертывания часто вызывает вопросы: как правильно собрать фронтенд, настроить веб-сервер и, главное, как автоматизировать получение SSL-сертификатов? В этой статье мы разберем надежную и современную архитектуру деплоя, объединив три мощных инструмента:Vite(сборщик),Docker(контейнеризация) иCaddy(веб-сервер). Такой стек позволяет забыть о ручной настройке Nginx и мучениях с Certbot, получая на выходе легковесный, безопасный и автоматически обновляемый сайт. Почему именно этот стек? Vite:Генерирует чистую статику (HTML, JS, CSS). Для работы сайта в продакшене Node.js не требуется, что критически снижает потребление ресурсов на сервере. Docker:Позволяет создать изолированное окружение. Мы используеммногоступенчатую сборку(multi-stage build), чтобы в итоговом образе остался только веб-сервер и статика, а не гигабайты node_modules. Caddy:Это веб-сервер нового поколения, написанный на Go. Его «киллер-фича» — автоматический HTTPS. Caddy сам получает и продлевает сертификаты Let's Encrypt через протокол ACME, требуя от вас лишь указать домен в конфиге. Шаг 1. Конфигурация Caddy: Безопасность и автоматизация В отличие от Nginx, конфигурация которого может занимать десятки строк для простого кейса, Caddyfile невероятно лаконичен. Мы настроим автоматическое перенаправление с HTTP на HTTPS, включим сжатие и зададим заголовки безопасности. Создайте в корне проекта файл Caddyfile: ``` # Домен читается из переменных окружения (.env) {$DOMAIN} { # Автоматическая выдача SSL-сертификата на указанную почту tls {$EMAIL} # Корневая директория для статики внутри контейнера # Путь /usr/share/caddy является стандартным для образа Caddy root * /usr/share/caddy # Запускаем файловый сервер, который сам определяет MIME-типы file_server # Включаем сжатие GZIP для экономии трафика encode gzip # Настройка кэширования статических ассетов (картинок, шрифтов, JS/CSS) # immutable позволяет браузеру не переспрашивать файлы при жестком обновлении header /assets/* Cache-Control: "public, max-age=31536000, immutable" header /*.css Cache-Control: "public, max-age=31536000, immutable" header /*.js Cache-Control: "public, max-age=31536000, immutable" # Логика маршрутизации для SPA (Single Page Application): # Если файл не найден, отдаем index.html (нужно для роутинга Vue/React) try_files {path} {path}/ /index.html # Блок безопасности: добавляем HTTP-заголовки защиты header { # HSTS: заставляем браузер всегда использовать HTTPS (на 2 года) Strict-Transport-Security "max-age=63072000" # Запрет на "угадывание" MIME-типа (защита от XSS) X-Content-Type-Options "nosniff" # Защита от кликджекинга (запрет встраивания сайта во фреймы) X-Frame-Options "DENY" # Защита от старых типов XSS-атак X-XSS-Protection "1; mode=block" } } ``` Шаг 2. Многоступенчатая сборка Docker Главная цель Dockerfile в этом сценарии — создать минимальный по размеру образ. Мы разделим процесс на два этапа:Builder(где крутится Node.js и собирается сайт) иRuntime(где работает только легкий Alpine Linux и Caddy). Создайте файл Dockerfile: ``` # ========================================== # ЭТАП 1: Сборка (Builder) # ========================================== FROM node:24-slim AS builder WORKDIR /app # Копируем только манифесты зависимостей. # Это позволяет кэшировать слой npm ci в Docker. COPY package*.json ./ # Используем npm ci вместо install для строгого соответствия package-lock RUN npm ci # Копируем исходники (в примере VitePress, папка docs) и запускаем билд COPY docs ./docs RUN npm run docs:build # Результат сборки появится в /app/docs/.vitepress/dist # ========================================== # ЭТАП 2: Продакшен (Runtime) # ========================================== FROM alpine:3.23.5 # Устанавливаем Caddy. Флаг --no-cache экономит место на диске. RUN apk add --no-cache caddy # Копируем ТОЛЬКО собранные статические файлы из этапа builder # Исходники, node_modules и исходный код проекта остаются за бортом! COPY --from=builder /app/docs/.vitepress/dist /usr/share/caddy # Копируем конфигурационный файл сервера COPY Caddyfile /etc/caddy/Caddyfile # Открываем стандартные порты для веба EXPOSE 80 443 # Запускаем Caddy с указанием конфига и адаптера CMD ["caddy", "run", "--config", "/etc/caddy/Caddyfile", "--adapter", "caddyfile"] ``` Шаг 3. Оркестрация через Docker Compose Для удобного управления контейнером, сетями и томами (volumes) используем docker-compose. Это позволит легко запускать и останавливать сервис одной командой. Создайте файл docker-compose.yml: ``` version: "3.8" services: caddy: ----------