Публикация на Beget и проверка HTTP
В предыдущем уроке мы добавили проверку готовой папки dist. Она обнаруживает потерянные адреса, отсутствующие ресурсы и повреждённую навигацию. Теперь рассмотрим вторую часть публикации: как эту папку отдаёт настоящий веб-сервер. Файл my/legacy/first.php может существовать на диске, но сервер решит обработать его как PHP или предложить посетителю скачивание. Такой результат локальная проверка файлов не обнаружит.
Разберём публикацию нашего учебного markdown-site на виртуальном хостинге Beget. Используем отдельный тестовый сайт, резервную копию исходного дерева и заранее определённый возврат. Это порядок, который помогает сохранить адреса при замене старой HTML-библиотеки, как в случае ProfessorWeb. Домены и имена папок ниже являются примерами; перед действиями нужно определить реальные пути собственного аккаунта.
Что передаётся на хостинг
На сервер нужно отправить содержимое dist: готовые страницы, ресурсы, поисковый индекс, Sitemap и robots.txt. Markdown, виртуальное окружение Python, build.py и файл проверки нужны для подготовки выпуска, а не для ответа посетителю. Поэтому корневая папка сайта не должна становиться копией всего репозитория.
В site.yaml должен находиться основной публичный домен, например https://example.com. Если тестовая копия открывается на https://preview.example.com, её HTML всё равно можно проверять с каноническими адресами основного домена. Затем тот же подготовленный выпуск переносится в рабочую папку без изменения содержания. Чтобы тестовый домен не создавал доступную поисковую копию, ограничьте его HTTP-аутентификацией или другим поддерживаемым хостингом способом доступа. Один robots.txt не защищает файлы от посетителей.
В текущей базе нет отдельной страницы ошибки. Добавим её через реестр, чтобы она получила общие хедер и футер. Перед collect_pages() в build.py разместите новую функцию:
def build_not_found(pages):
add_page(pages, {
"title": "Страница не найдена",
"description": "Запрошенная страница отсутствует на сайте.",
"permalink": "/404.html",
"noindex": True,
"body": """<p>Проверьте адрес страницы или найдите материал в каталоге.</p>
<p><a href="/catalog/index.html">Перейти в каталог</a></p>""",
})
Затем замените collect_pages() целиком:
def collect_pages(documents):
pages = {document["permalink"]: document for document in documents}
build_catalog(documents, pages)
build_search(documents, pages)
build_not_found(pages)
validate_discovery(pages)
return pages
main() остаётся прежним. Проверка из прошлого урока также использует collect_pages(), поэтому увидит новый файл без собственного списка дополнительных адресов. После сборки ожидаются девять страниц, три записи внутреннего поиска и семь адресов в Sitemap. Страница ошибки, как и поиск, имеет noindex и не входит в карту.
При прямом запросе /404.html сервер обычно вернёт 200: вы попросили существующий файл. Чтобы неизвестный адрес возвращал 404 с таким же оформлением, требуется отдельное серверное правило. Текст ошибки и код ответа — разные части результата.
Тестовый сайт и резервная копия
По руководству Beget по управлению сайтами, новый сайт создаёт свою папку с public_html; домен или поддомен прикрепляется к выбранному сайту. Создайте отдельное место для тестовой копии и прикрепите к нему тестовый поддомен. Не привязывайте его к рабочему public_html, если собираетесь менять серверные правила: тест должен иметь собственную конфигурацию.
Перед публикацией сохраните текущее дерево рабочего сайта целиком, включая скрытые файлы, например .htaccess. В резервную копию должны попасть не только статьи, но и рисунки, старые загрузки и правила перенаправления. Для смешанного сайта дополнительно сохраняют базы данных и внешние настройки; папка статических страниц сама по себе их не заменяет.
Копию скачайте на компьютер. Архив размещайте вне любой публичной корневой папки: файл внутри public_html может быть получен по URL. Например, для уже скачанного дерева original-download локальный архив можно подготовить так:
mkdir -p backups
tar -czf backups/site-before-release-2026-10-10.tar.gz -C original-download .
tar -tzf backups/site-before-release-2026-10-10.tar.gz
Первая команда создаёт отдельную папку резервных копий, вторая упаковывает содержимое исходного дерева вместе со скрытыми файлами. Третья выводит список записей архива. До переключения полезно распаковать копию в отдельное место и сравнить несколько важных страниц и ресурсов с оригиналом. Архив, содержимое которого никто не может прочитать, не даёт уверенного возврата.
Сохранённую рабочую папку также можно оставить на сервере рядом с будущим выпуском, если места достаточно. Проверяйте, что к ней не прикреплён другой публичный домен. Местоположение вне текущего public_html уменьшает риск случайной загрузки по основному адресу, но не отменяет проверки других привязок аккаунта.
Статический HTML под прежним расширением PHP
Наш генератор записывает в /my/legacy/first.php обычный HTML. Наличие окончания .php сохраняет исторический URL и не делает этот файл программой. Для публикации требуется, чтобы сервер отдавал его как HTML без выполнения PHP.
Здесь нельзя предложить универсальную настройку для любого аккаунта Beget. Запрос может проходить через nginx, Apache и настроенный обработчик PHP; правила .htaccess действуют на стороне Apache и только там, где это разрешено конфигурацией. Сначала уточните используемую цепочку и возможность переопределить обработчик для отдельного статического сайта. Если сервер обрабатывает .php раньше Apache или запрещает нужные директивы, настройку нужно согласовать с поддержкой хостинга.
Для отдельного сайта на Apache 2.4, где разрешены эти директивы .htaccess, создайте в учебном проекте public/.htaccess с таким содержимым:
DirectoryIndex index.html
ErrorDocument 404 /404.html
<FilesMatch "\.php$">
SetHandler default-handler
ForceType text/html
</FilesMatch>
Этот листинг относится только к папке выпуска, содержащей сгенерированные статические файлы. Он не подходит для смешанной рабочей папки с настоящими PHP-скриптами: их исходный код нельзя отдавать посетителю. В нашем проекте старый HTML-оригинал находится отдельно, а в dist не копируются обработчики форм, пароли или служебные PHP-файлы.
default-handler — встроенный обработчик статического содержимого Apache. ForceType задаёт для совпавшего файла MIME-тип text/html, который помогает браузеру интерпретировать ответ как документ. Назначение обработчика описано в документации Apache, а параметры SetHandler и ForceType — в справочнике директив. Правило не заменяет .php на новый URL и не создаёт редирект.
DirectoryIndex разрешает открывать index.html при запросе папки; например, /catalog/ показывает /catalog/index.html. Для нашей главной также ожидается доступ и по /, и по /index.html, при этом canonical остаётся выбранным постоянным адресом. Если потребуется сделать один вариант редиректом, это отдельное правило, которое проверяют на отсутствие циклов.
ErrorDocument использует локальный путь /404.html. Подстановка полного внешнего адреса могла бы превратить обработку ошибки в перенаправление. Сервер должен сохранить статус исходного неизвестного запроса и использовать подготовленный HTML как тело ответа. Назначение этой директивы объясняет документация Apache по ErrorDocument.
Теперь сформируйте выпуск и выполните локальную проверку из предыдущего урока. Затем загрузите содержимое dist в public_html отдельного тестового сайта, сохранив вложенные папки и скрытый .htaccess. Если загрузить саму папку dist целиком, адрес статьи окажется под дополнительным /dist/, а корневые ссылки на ресурсы перестанут совпадать с размещением.
Проверка через настоящий HTTP
Откройте тестовую копию после настройки HTTPS и доступа. Проверяйте ответы во вкладке Network инструментов браузера: вид страницы не всегда показывает ошибку статуса или неверный MIME-тип. Обязательно запросите следующие адреса:
| Запрос | Ожидаемое поведение |
|---|---|
/index.html |
200, главная и общие элементы страницы |
/articles/second.html |
200, правильная статья и оба соседа |
/my/legacy/first.php |
200, HTML отображается как страница |
/assets/site.css |
200, ресурс распознаётся как CSS |
/assets/search-index.json |
200, JSON доступен поиску |
/catalog/ |
открывается каталог из index.html |
/missing-release-check-2026.html |
404, оформленная страница ошибки |
/404.html |
существующий файл, обычно 200, метатег noindex |
Для сохранённого .php важны сразу три наблюдения: статус 200, заголовок Content-Type: text/html с возможным параметром кодировки и ожидаемое содержание страницы. Файл не должен скачиваться или содержать служебный исходный код. Если это происходит, остановитесь на тестовом сайте и исправьте серверную выдачу; изменение канонической ссылки не решит такую ошибку.
Если неизвестный адрес возвращает красивую страницу со статусом 200, сервер сообщает, что ресурс существует. Это неверное условие для удалённого материала. Также не заменяйте любую ошибку переходом на главную: посетитель потеряет контекст, а диагностика перестанет отличать отсутствующий урок от существующего.
Затем отправьте запрос внутреннего поиска, перейдите по найденной ссылке, проверьте пустой результат и откройте Sitemap. На тестовом домене canonical и loc должны содержать основной домен, указанный при сборке. Внешние системы вебмастера пока не получают тестовую карту: сначала должен быть подтверждён рабочий выпуск.
Переключение и возврат
В рабочем сайте подготовьте соседнюю папку для нового выпуска. Например, внутри подтверждённой папки сайта структура перед переключением может выглядеть так:
site-directory/
public_html/ прежняя версия
public_html_next/ проверенный новый выпуск
Запишите дату, идентификатор версии исходников, имя резервного архива и фактический путь каждой папки. Если конфигурация тестового сайта содержала ограничение доступа, не переносите его автоматически на рабочий домен. При этом правила статического .php и 404 должны соответствовать проверенной конфигурации рабочего сайта.
В выбранный момент сохраните прежнюю папку под уникальным именем, например public_html_previous_2026_10_10, а подготовленной папке присвойте имя public_html. Переименование двух папок — две операции; между ними возможен короткий промежуток недоступности. Если нужен выпуск без такого промежутка, потребуется поддерживаемый хостингом механизм переключения корня или иная заранее проверенная схема. Не следует называть два ручных переименования атомарным обновлением.
После переключения повторите HTTP-проверки уже на основном домене. Например, обычный GET можно посмотреть так:
curl --dump-header - --output /tmp/legacy-response.html https://example.com/my/legacy/first.php
curl --silent --show-error --output /tmp/missing-response.html --write-out '%{http_code}\n' https://example.com/missing-release-check-2026.html
Первая команда сохраняет тело ответа отдельно и показывает заголовки, вторая выводит статус отсутствующей страницы; ожидается 404. Проверяйте фактический домен вместо учебного example.com. Для защищённого тестового адреса потребуется авторизация, иначе корректным ответом может быть 401 — это проверка доступа, а не результата выдачи статьи.
При существенной ошибке сохраните новый выпуск отдельно и верните прежней папке имя public_html. После возврата снова проверьте главную, старый адрес и нужные ресурсы. Не удаляйте предыдущую версию сразу после успешного открытия главной: длинная библиотека может содержать проблемы, которые обнаружатся на менее посещаемых страницах.
Наконец передайте рабочий Sitemap в используемые инструменты вебмастера и наблюдайте за обходом, ошибками страниц и трафиком. Сохранение адресов и правильных ответов сервера позволяет продолжать работу со старой библиотекой, но само по себе не обещает восстановление прежних позиций. Теперь у учебного проекта есть полный путь сопровождения: Markdown и шаблоны в исходниках, проверяемая папка выпуска, тестовая публикация и определённый способ возврата к предыдущей версии.