Содержание

Настройка публикации статического сайта из VS Code

Сопоставим основные бесплатные и бюджетные варианты хостинга статики.

СпособСвой доменSSLКак публикуетсяОграничения
GitHub Pagesдаавтоматическиgit push + Actionsлимиты на объем репозитория и трафик; на бесплатном плане – лишь публичный контент
Netlify / Vercelдаавтоматическиgit push или CLIсверх бесплатного лимита минуты сборки и трафик платные
Cloudflare Pagesдаавтоматическиgit push или Wranglerконфигурация в веб-панели, собственные правила кеширования
AWS S3 + CloudFrontдачерез ACMaws s3 syncручная конфигурация бакета, политик, дистрибуции и сертификата; оплата за каждый гигабайт трафика
VPS + NginxдаLet’s Encrypt вручнуюrsync/scpобновления, сертификаты и безопасность – ответственность администратора
WebShieldдаавтоматическиwebshield sites publishлимиты хранилища зависят от тарифного плана

Как загрузить сайт на GitHub Pages. Собранные файлы кладутся в ветку gh-pages либо в каталог docs/ ветки main, после чего Pages включается в параметрах репозитория. Для сборки на серверах GitHub создается workflow в Actions. Публикация неизменно идет через git.

Публикация сайта через Netlify и деплой проекта на Vercel устроены одинаково: подключается репозиторий, указываются команда сборки и папка с результатом, и затем любой push запускает деплой. Готовую папку можно загрузить и без репозитория с помощью CLI (netlify deploy, vercel). Бесплатный план ограничен минутами сборки и объемом трафика.

Cloudflare Pages воспроизводит шаги Netlify: подключаем репозиторий, прописываем команду сборки, получаем домен *.pages.dev и подключаем свой. Бесплатный план щедрый, но параметры и правила кеширования настраиваются исключительно в панели Cloudflare.

Хостинг статического сайта AWS S3 требует наибольшего объема ручной настройки: бакет, статический веб-хостинг, политика доступа, CloudFront для HTTPS и CDN, сертификат в ACM. Сверх бесплатного лимита CloudFront любой гигабайт исходящего трафика тарифицируется отдельно без верхней границы, так что затраты нужно контролировать с помощью биллинг-алертов.

VPS с Nginx обеспечивает полный контроль, однако обновление системы, продление сертификатов и безопасность остаются на вас. Пример такой конфигурации с ограниченным доступом разобран в статье про аутентификацию по клиентским сертификатам на Nginx.

Дальше настроим схему, при которой сборка, публикация и проверка выполняются, не покидая VS Code.


Для примера возьмем сайт на Hugo и хостинг WebShield с его клиентом командной строки. Руководство подходит для любого генератора статических сайтов (Astro, Next.js, Nuxt, Eleventy, Docusaurus, MkDocs, VitePress) и для сайта из обыкновенных HTML-файлов – меняется лишь каталог с результатом сборки.

Что нужно:

  • VS Code (или Cursor, VS Codium);
  • установленный генератор и проект, который собирается на локальной машине;
  • домен с делегированием на сервис WebShield;
  • учетная запись и личный API-токен wsk_… из панели управления.

У регистратора домена заменяем NS-серверы на nsbox.webshield.pro и nshub.webshield.pro. Старые NS-записи удаляем, в противном случае часть запросов станет уходить на прежние серверы и сайт будет открываться через раз. Применение делегирования может занять до 48 часов.

Структура проекта на Hugo выглядит так:

my-site/
├── content/          # статьи в Markdown
├── layouts/          # шаблоны
├── static/           # файлы, попадающие в сборку как есть
├── themes/           # тема
├── hugo.toml         # конфигурация, здесь же baseURL
└── public/           # результат сборки — то, что мы публикуем

Каталог public/ появляется при сборке, так что вносим его в .gitignore вместе с временными файлами Hugo.

public/
resources/
.hugo_build.lock

Проверяем, что в hugo.toml задан верный адрес сайта. Hugo подставляет его в абсолютные ссылки при сборке, и при неправильном значении на сайте не подгрузятся стили и картинки.

baseURL = "https://example.com/"

Для комфортной работы стоит установить расширения VS Code:

  • Even Better TOML и YAML – подсветка и проверка front matter и конфигов;
  • Front Matter CMS – редактирование метаданных статей через форму;
  • Markdown All in One – таблицы, содержание, горячие клавиши;
  • Live Preview – просмотр собранной статики без сервера генератора.

Стартуем предпросмотр во встроенном терминале VS Code (Ctrl+`). Флаг -D включает показ черновиков.

hugo server -D

Сайт откроется по адресу http://localhost:1313 и обновляется при каждом сохранении файла.


Клиент распространяется одним исполняемым файлом без зависимостей. Исходный код и релизы лежат в репозитории webshield-cli на GitHub, полный перечень команд – в документации по консольному клиенту. Готовые бинарники есть для Linux (x86_64 и aarch64), macOS (Intel и Apple Silicon) и Windows (x86_64). Все команды выполняем во встроенном терминале VS Code.

Запускаем скрипт установки. Он определит ОС и архитектуру, скачает свежий релиз, сверит контрольную сумму SHA-256, скопирует бинарный файл в ~/.local/bin и настроит автодополнение.

curl -fsSL https://raw.githubusercontent.com/webshieldpro/webshield-cli/main/install.sh | sh

Проверяем результат установки.

webshield --version

Если команда не находится, прописываем каталог в PATH в файле ~/.bashrc (Linux) или ~/.zshrc (macOS) и перезапускаем VS Code.

export PATH="$HOME/.local/bin:$PATH"

Для zsh установщик в конце выведет строку, которую надо добавить в fpath, чтобы заработало автодополнение.

Скачиваем архив со страницы релизов, распаковываем и вносим каталог в пользовательский PATH. В PowerShell:

$ver = "1.0.2"
$dir = "$env:LOCALAPPDATA\Programs\webshield"
New-Item -ItemType Directory -Force -Path $dir | Out-Null
Invoke-WebRequest -Uri "https://github.com/webshieldpro/webshield-cli/releases/download/v$ver/webshield-$ver-x86_64-pc-windows-gnu.zip" -OutFile "$env:TEMP\webshield.zip"
Expand-Archive -Path "$env:TEMP\webshield.zip" -DestinationPath $dir -Force
[Environment]::SetEnvironmentVariable("Path", "$([Environment]::GetEnvironmentVariable('Path','User'));$dir", "User")

Полностью перезапускаем VS Code и проверяем, что установка прошла успешно.

webshield --version

Активируем автодополнение для PowerShell.

webshield completion powershell >> $PROFILE

Если вы используете WSL, устанавливайте Linux-версию скриптом и открывайте проект в режиме WSL: Remote.

Если запуск скриптов из интернета запрещен или для вашей платформы нет готового бинарника, компилируем клиент из исходного кода. Потребуется только Rust.

git clone https://github.com/webshieldpro/webshield-cli
cd webshield-cli
cargo build --release
mv target/release/webshield ~/.local/bin/

При ручной установке или нестандартной оболочке генерируем скрипты автодополнения. Поддерживаются bash, zsh, fish, PowerShell, elvish и nushell.

webshield completion bash > ~/.local/share/bash-completion/completions/webshield
webshield completion zsh > ~/.zsh/completions/_webshield
webshield completion fish > ~/.config/fish/completions/webshield.fish

Выпускаем токен в личном кабинете в разделе Настройки → API-токены. Выдаем необходимые права и, если нужно, ограничиваем токен конкретным доменом или сайтом.

Сохраняем токен в профиль и проверяем доступ.

webshield auth login
webshield auth status

Профили лежат в ~/.config/webshield/config.toml. Выбрать профиль можно флагом --profile или переменной WS_PROFILE. Токен также можно передать флагом --token или переменной WS_TOKEN.


Просматриваем список доменов и состояние делегирования. Статус должен быть «делегирован».

webshield domains list
webshield domains check example.com

Если домена нет в списке, добавляем его.

webshield domains add example.com

Создаем сайт на главном домене example.com и проверяем, что он отобразился в списке.

webshield sites create example.com --domain example.com
webshield sites list
Инфо
Важный момент! Сайт привязывается к апексу домена или к существующей DNS-записи A, AAAA или CNAME. Для поддомена предварительно создаем запись, например: webshield dns add example.com docs CNAME example.com. Для того же имени не должно быть включено проксирование.

Сайт создается в статусе «Отключен» и недоступен посетителям до первой публикации.

Создавать второй сайт для www не нужно. Добавляем DNS-запись и настраиваем редирект на основной домен, чтобы у сайта была одна каноническая версия.

webshield dns add example.com www CNAME example.com
webshield proxy set www.example.com --domain example.com \
    --mode redirect --redirect-target example.com

В карточке сайта задаем параметры до первой публикации. Если сайт уже опубликован, изменения применяются сразу, без повторной публикации. Подробное описание параметров, ограничений и API – в документации по размещению статического сайта.

  • Генератор сайта – пресет для Hugo, Jekyll, Astro, Gatsby, Next.js export, SPA или простого HTML. Заполняет остальные параметры соответствующими значениями.
  • Чистые URL – правило сопоставления адресов с файлами. «Индекс каталога» (/about/about/index.html) для Hugo, Jekyll, Astro, Eleventy и Gatsby. «Расширение .html» (/about/about.html) для экспорта Next.js.
  • Страница 404 – файл, отдаваемый со статусом 404, например 404.html.
  • Режим SPA – возвращать index.html со статусом 200 на незнакомые пути. Необходим для одностраничных приложений с маршрутизацией на клиенте.
  • Публиковать по HTTPS – выдача сертификата и редирект с HTTP на HTTPS. Сертификат выпускается при публикации и обновляется автоматически.
  • Защита от ботов – выключена, проверка браузера или капча. Подробности в статье про защиту сайта от ботов.

Собираем сайт и выполняем публикацию в тестовом режиме с флагом --dry-run. Файлы на сервер не отправляются, выводится лишь список добавляемых, измененных и удаляемых файлов.

hugo --minify
webshield sites publish example.com --dir ./public --dry-run
Предупреждение
Важный момент! Публикация синхронизирует каталог целиком: файлы, отсутствующие в --dir, будут удалены с сайта. Перед первой публикацией обязательно просматривайте вывод --dry-run, чтобы удостовериться, что публикуется public/, а не корень проекта.

Если все корректно, публикуем сайт.

webshield sites publish example.com --dir ./public

Клиент сравнивает хеши локальных файлов с файлами на сервере, отправляет только изменения и выкладывает новую версию атомарно – посетители не столкнутся с частично обновленным сайтом. При первой публикации DNS-запись хоста переключается на инфраструктуру WebShield (прежняя запись восстановится при отключении сайта) и выдается TLS-сертификат.

Проверяем список файлов на сервере.

webshield sites files example.com

Для прочих генераторов меняется только каталог сборки.

webshield sites publish example.com --dir ./dist    # Astro, Vite, Nuxt
webshield sites publish example.com --dir ./build   # Next.js export, CRA
webshield sites publish example.com --dir ./site    # MkDocs

Создаем в корне проекта файл .vscode/tasks.json с задачами сборки и публикации.

{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "build",
      "type": "shell",
      "command": "hugo --minify",
      "problemMatcher": []
    },
    {
      "label": "publish",
      "type": "shell",
      "command": "webshield sites publish example.com --dir ./public",
      "dependsOn": "build",
      "group": { "kind": "build", "isDefault": true },
      "presentation": { "reveal": "always", "panel": "dedicated" },
      "problemMatcher": []
    },
    {
      "label": "publish (dry-run)",
      "type": "shell",
      "command": "webshield sites publish example.com --dir ./public --dry-run",
      "dependsOn": "build",
      "problemMatcher": []
    }
  ]
}

Не забудьте поменять все example.com на свой домен. Теперь Ctrl+Shift+B собирает и выкладывает сайт. Задачу publish (dry-run) запускаем через Ctrl+Shift+P → Tasks: Run Task.

Отдельный шаг build обязателен: hugo server прописывает в baseURL адрес http://localhost:1313/, и без повторной сборки на сайт попадут ссылки на localhost.

Файл .vscode/tasks.json не хранит секретов, его можно закоммитить в репозиторий. Задачи выполняются в Linux, macOS и Windows, если webshield находится в PATH.

Для тестовой среды добавляем задачу с другим адресом и профилем.

{
  "label": "publish (staging)",
  "type": "shell",
  "command": "webshield sites publish staging.example.com --dir ./public --profile staging",
  "dependsOn": "build",
  "problemMatcher": []
}

Предварительно создаем для него DNS-запись и сайт.

webshield dns add example.com staging CNAME example.com
webshield sites create staging.example.com --domain example.com

Чтобы запускать публикацию своим сочетанием клавиш, открываем File → Preferences → Keyboard Shortcuts → Open Keyboard Shortcuts (JSON) и добавляем:

{
  "key": "ctrl+alt+p",
  "command": "workbench.action.tasks.runTask",
  "args": "publish"
}

Открываем сайт в браузере и проверяем, что сертификат выпущен на ваш домен. Статистику и расход ресурсов просматриваем из терминала.

webshield stats summary example.com --range 7d   # сводка трафика и запросов
webshield stats bans example.com                 # активные баны и проверки
webshield billing usage example.com              # расход относительно лимита тарифа

Для скриптов и мониторинга используем вывод в JSON.

webshield -o json stats summary example.com --range 24h | jq '.requests'

Кеш на edge-серверах сбрасывается автоматически при каждой публикации. Если браузер показывает устаревшую версию страницы, перезагружаем ее с очисткой кеша (Ctrl+Shift+R).


Сайт постоянно обновляется. Рано или поздно возникает необходимость в переносе или переименовании страниц. Когда сайт размещен на собственном сервере или хостинге – с этим проблем нет, редирект можно настроить прямо в веб сервере или PHP. На статическом хостинге тоже есть возможность задавать правила переадресации. Для этого пишем их в файле _redirects в корне публикуемого каталога, как описано в документации. Для Hugo его нужно разместить в каталоге static/.

# По умолчанию 301 редирект, но можно задать явно
/contacts-old       /contacts
/blog/*             /posts/:splat   301
/black-friday-sale  /               302
/go/telegram        https://t.me/channel

Формат совместим с Netlify и Cloudflare Pages, при переносе проблем возникнуть не должно.

Статические сайты идеально подходят для лендингов и сайтов-визиток – на них обычно нет авторизации и пользовательских данных. А форму обратной связи для сбора контактов можно давить прямо на статическом сайте, без своего бэкенд сервера. Подключаем форму, как написано в документации.

<form action="/webshieldpro/forms/feedback" method="post">
  <p><label>Имя<br><input name="name" required></label></p>
  <p><label>Электронная почта<br><input name="email" type="email" required></label></p>
  <p><label>Сообщение<br><textarea name="message" rows="5" required></textarea></label></p>
  <p><button type="submit">Отправить</button></p>
</form>
<script src="/webshieldpro/forms.js" defer></script>

Здесь имя feedback может быть произвольным. По нему потом можно смотреть в личном кабинете, какую форму заполняли.

Для Hugo HTML нужно выносить из Markdown, поэтому форму размещаем в шаблоне или шорткоде, например layouts/shortcodes/contact-form.html, и вставляем на страницу через {{< contact-form >}}.

При публикации сайта, форма автоматически обнаружится и добавится в блок Формы карточки сайта. Все сообщения, приходящие на эту форму будут отправлены на ваш почтовый ящик и сохранены в разделе Сообщения, т.е. их всегда можно посмотреть в личном кабинете.


Если сайт редактируют несколько человек, публикацию выносим в CI. В карточке сайта выпускаем токен публикации. Он работает только для этого сайта и показывается один раз – сохраняем его в секреты репозитория, например WS_PUBLISH_TOKEN.

Прописываем шаг в GitHub Actions.

- name: Publish site
  env:
    WS_TOKEN: ${{ secrets.WS_PUBLISH_TOKEN }}
  run: |
    curl -fsSL https://raw.githubusercontent.com/webshieldpro/webshield-cli/main/install.sh | sh
    ~/.local/bin/webshield sites publish example.com --dir ./public

Если сборка выгружает результат в S3-бакет, публикуем сайт непосредственно из него. Содержимое префикса заменяет сайт и сохраняется в виде новой версии, после чего исходный бакет можно удалить.

webshield sites publish-from-bucket example.com --bucket web --path public/

Каталог с исполняемым файлом не внесен в PATH. В Linux и macOS добавляем ~/.local/bin в ~/.bashrc или ~/.zshrc, в Windows проверяем пользовательскую переменную Path. После правки полностью перезапускаем VS Code: встроенный терминал получает окружение, с которым был запущен редактор.

Проверяем, что видит терминал.

echo $PATH                # Linux, macOS
$env:Path -split ';'      # PowerShell

Терминал VS Code в Linux запускается как не-login оболочка и не загружает ~/.profile, поэтому строку нужно добавлять в ~/.bashrc.

Токен просрочен, отозван, выпущен для другого сайта или не обладает правами на запись. Проверяем текущий профиль и доступ.

webshield auth status

Если ошибка возникает только в CI, убеждаемся, что:

  • секрет пробрасывается в переменные окружения шага, а не только job;
  • workflow запущен не из форка – для таких запусков GitHub не выдает секреты;
  • в репозитории отсутствует переменная с тем же именем, что у секрета.

Убедиться, что токен дошел до шага, можно без вывода его значения: echo "${#WS_TOKEN}" выведет длину строки. Значение 0 говорит о том, что переменная пустая.

На сайте видны content/, themes/ и прочие исходники – при публикации задан --dir . вместо --dir ./public. Публикуем снова с верным путем, новая версия полностью заменит прежнюю.

В настройках сайта выбран неверный режим «Чистые URL». Проверяем, какой вариант файла есть на сервере.

curl -I https://example.com/about/index.html
curl -I https://example.com/about.html

Если 200 пришел в ответ на первый запрос, выбираем «Индекс каталога», если на второй – «Расширение .html».

В конфигурации генератора baseURL не указан или указан неверно. Исправляем значение, пересобираем сайт и снова публикуем. Повторная публикация без пересборки бесполезна, так как адреса уже зашиты в файлы.

Делегирование пока не обновилось или у регистратора остались старые NS-серверы. Сертификат не выпустится, пока домен не направлен на WebShield.

webshield domains check example.com
dig NS example.com +trace                    # фактическое делегирование
dig example.com @nsbox.webshield.pro         # ответ сервера WebShield

Если в выводе +trace видны лишние серверы, удаляем их у регистратора и дожидаемся истечения TTL.

Проверяем лимиты тарифа на объем хранилища и размер файла. Исполняемые файлы и установщики (.exe, .msi, .apk, .bat и т.п.) можно размещать только на платном тарифе домена, на бесплатном их загрузка отклоняется. Архивы (.zip, .tar.gz) разрешены на любом тарифе. Перечень отправляемых файлов можно заранее просмотреть с флагом --dry-run.

Похожее