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

Куда выкладывать статический сайт
Сопоставим основные бесплатные и бюджетные варианты хостинга статики.
| Способ | Свой домен | SSL | Как публикуется | Ограничения |
|---|---|---|---|---|
| GitHub Pages | да | автоматически | git push + Actions | лимиты на объем репозитория и трафик; на бесплатном плане – лишь публичный контент |
| Netlify / Vercel | да | автоматически | git push или CLI | сверх бесплатного лимита минуты сборки и трафик платные |
| Cloudflare Pages | да | автоматически | git push или Wrangler | конфигурация в веб-панели, собственные правила кеширования |
| AWS S3 + CloudFront | да | через ACM | aws 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
Клиент распространяется одним исполняемым файлом без зависимостей. Исходный код и релизы лежат в репозитории webshield-cli на GitHub, полный перечень команд – в документации по консольному клиенту. Готовые бинарники есть для Linux (x86_64 и aarch64), macOS (Intel и Apple Silicon) и Windows (x86_64). Все команды выполняем во встроенном терминале VS Code.
Linux и macOS
Запускаем скрипт установки. Он определит ОС и архитектуру, скачает свежий релиз, сверит контрольную сумму 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, чтобы заработало автодополнение.
Windows
Скачиваем архив со страницы релизов, распаковываем и
вносим каталог в пользовательский 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 listA, AAAA или CNAME.
Для поддомена предварительно создаем запись, например: webshield dns add example.com docs CNAME example.com.
Для того же имени не должно быть включено проксирование.Сайт создается в статусе «Отключен» и недоступен посетителям до первой публикации.
Переадресация с www
Создавать второй сайт для 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. Сертификат выпускается при публикации и обновляется автоматически.
- Защита от ботов – выключена, проверка браузера или капча. Подробности в статье про защиту сайта от ботов.
Публикация из VS Code
Проверка и публикация
Собираем сайт и выполняем публикацию в тестовом режиме с флагом --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Задача публикации в VS Code
Создаем в корне проекта файл .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).
Переадресации и формы
Переадресации в _redirects
Сайт постоянно обновляется. Рано или поздно возникает необходимость в переносе или переименовании страниц.
Когда сайт размещен на собственном сервере или хостинге – с этим проблем нет, редирект можно настроить прямо в
веб сервере или 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
Если сайт редактируют несколько человек, публикацию выносим в 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/Решение проблем
webshield: command not found
Каталог с исполняемым файлом не внесен в 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.
Ошибка 401 при публикации
Токен просрочен, отозван, выпущен для другого сайта или не обладает правами на запись. Проверяем текущий профиль и доступ.
webshield auth statusЕсли ошибка возникает только в CI, убеждаемся, что:
- секрет пробрасывается в переменные окружения шага, а не только job;
- workflow запущен не из форка – для таких запусков GitHub не выдает секреты;
- в репозитории отсутствует переменная с тем же именем, что у секрета.
Убедиться, что токен дошел до шага, можно без вывода его значения: echo "${#WS_TOKEN}" выведет длину строки.
Значение 0 говорит о том, что переменная пустая.
Опубликован не тот каталог
На сайте видны content/, themes/ и прочие исходники – при публикации задан --dir . вместо --dir ./public.
Публикуем снова с верным путем, новая версия полностью заменит прежнюю.
Внутренние страницы возвращают 404
В настройках сайта выбран неверный режим «Чистые 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.

