Как обновить старый сайт и сохранить его URL
Старый сайт может оставаться полезным, даже если его шаблоны трудно менять, навигация устарела, а новые статьи приходится собирать вручную. Обновление проще контролировать, когда адрес каждого материала сохраняется: ссылка из поисковой выдачи, закладки или чужого сайта продолжает вести к нужной статье.
В октябре 2026 года мы обновили ProfessorWeb: перенесли материалы в Markdown, сделали общие шаблоны, исправили пагинацию, добавили каталог и поиск, затем опубликовали статическую сборку на Beget. Адреса прежних статей, в том числе заканчивающиеся на .php, остались прежними.
Ниже — последовательность этой работы, примеры файлов и проверки, которые можно повторить для своего сайта. Сохранение адресов снижает риск технической потери страниц при обновлении. Динамику поисковых переходов нужно оценивать отдельно: прежний URL сам по себе не гарантирует прежние позиции или посещаемость.
Кейс и примеры проверены 10 октября 2026 года. Числа относятся к первой публикации обновлённой версии; с добавлением статей размер каталога и карты сайта меняется.
1. Сначала определите, что именно переносится
У ProfessorWeb были публичные страницы с текстами, таблицами, изображениями и примерами кода. Прежний сервер собирал их с помощью PHP, но основное содержимое можно было сохранить в готовом HTML и отдавать без исполнения PHP. Такие материалы подошли для статической сборки.
Одновременно в исходниках оставались форум и демонстрации отправки файлов на сервер. Их нельзя восстановить одной генерацией HTML: им нужны обработчики, состояние и, возможно, база данных. Мы сохранили исходные файлы, но не считали эти функции восстановленными.
Перед выбором технологии составьте два списка:
- Что можно отдавать готовыми файлами: статьи, оглавления, изображения, клиентские демонстрации.
- Что требует сервера: авторизация, отправка форм, форум, платежи, загрузка файлов, персональные данные пользователя.
Для каждой серверной функции решите, где она будет работать после обновления. Статический поиск по статьям можно реализовать в браузере; сохранение сообщения на сервере таким способом не заменяется.
2. Сделайте копию, из которой можно восстановить сайт
Перед изменениями мы сохранили исходные файлы хостинга и публичные HTML-ответы. Перед самой публикацией создали ещё один свежий архив: 12 488 файлов, около 705 МБ. Его целостность проверили полностью, а локальную копию сверили по SHA256.
Файловый архив и сохранённые HTML решают разные задачи. Первый сохраняет исходники и настройки; вторые позволяют сравнить текст и примеры с новой сборкой. Если сайт использует базу данных, нужен отдельный дамп и проверка восстановления. В нашем свежем архиве SQL-дампов не было; в панели текущего аккаунта на момент публикации отображалось ноль баз данных.
Для локальной проверки ZIP и контрольной суммы можно использовать:
python3 -m zipfile -t site-before-update.zip
shasum -a 256 site-before-update.zip
Первая команда проверяет читаемость и целостность записей архива. Вторая помогает убедиться, что копирование не изменило файл. Эти проверки не заменяют пробное восстановление в отдельной папке и запуск зависимых сервисов.
Храните резервные копии вне публичной папки сайта и сделайте независимую копию на другом носителе. В Git помещайте рабочие исходники и настройки без секретов; архив хостинга может содержать пароли и другие закрытые данные.
3. Соберите реестр прежних адресов
Одной старой карты сайта оказалось недостаточно. Мы объединили адреса из сохранённых страниц, sitemap и выгрузок Яндекс Вебмастера. В данных Яндекса нашлась статья, отсутствовавшая в старом sitemap: /my/csharp/charp_theory/level13/3_10.php. Её тоже восстановили.
Для своего сайта используйте несколько источников: карту сайта, оглавления, журналы сервера, страницы с переходами в аналитике, данные поисковых панелей и известные внешние ссылки. Учитывайте регистр символов, расширения, завершающий слеш и параметры, от которых зависит содержимое.
Для каждого адреса запишите ожидаемое поведение:
| Старый адрес | Что должно быть после обновления |
|---|---|
/my/LINQ/linq_xml/level7/7_1.php |
Та же статья, HTTP 200, тип text/html |
/my/ASP_NET/base/level1/base_aspnet_index.php |
Прежнее оглавление и рабочие ссылки, HTTP 200 |
/my/csharp/charp_theory/level1/search_result.php?valsearch=LINQ |
Рабочий поиск с сохранённым параметром |
/404/404.php |
Страница ошибки с настоящим HTTP 404 |
Наш реестр защищает 1715 старых адресов: это страницы и ресурсы, а не 1715 самостоятельных статей. Сборка проверяет, что защищённые материалы не удалены, не превращены в черновики и не получили запрет индексации. Для служебных страниц предусмотрены отдельные правила.
4. Отделите адрес статьи от её файла и рубрики
Имя Markdown-файла удобно для редактора. Публичный адрес важен для читателя. Они не обязаны совпадать.
Например, в ProfessorWeb материал хранится в content/my/LINQ/linq_xml/level7/7_1.php.md, а адрес задаётся параметром permalink. Ниже сокращённый пример метаданных для такого маршрута; заголовок взят условный:
---
title: Пример статьи о LINQ to XML
permalink: /my/LINQ/linq_xml/level7/7_1.php
layout: article.html
draft: false
---
При сборке создаётся файл dist/my/LINQ/linq_xml/level7/7_1.php с готовым HTML. Расширение осталось прежним, хотя PHP-код для показа статьи больше не нужен.
Новые рубрики каталога содержат ссылки на существующие материалы. Перенос статьи в другую рубрику меняет её положение в каталоге, но не требует нового permalink и второй копии текста.
Часть старых материалов содержит сложную HTML-разметку и демонстрации. Мы оставили такие фрагменты внутри Markdown, чтобы сохранить таблицы, примеры и поведение. Полное превращение каждого фрагмента в чистый Markdown не было условием переноса.
5. Сделайте общие шаблоны и восстановите связи
Хедер и футер мы вынесли в общие файлы:
layouts/partials/site-header.html
layouts/partials/site-footer.html
layouts/partials/head.html
layouts/article.html
После изменения общего блока достаточно пересобрать сайт. Старые и новые статьи получают одинаковую навигацию и оформление. Самостоятельные учебные демонстрации могут сохранять свою оболочку.
Отдельно мы исправили последовательности учебных страниц. Список материалов в data/collections.yaml определяет номера, предыдущую и следующую страницы. Например:
/my/csharp/charp_theory/level1/articles:
items:
- /my/csharp/charp_theory/level1/1_1.php
- /my/csharp/charp_theory/level1/1_2.php
- /my/csharp/charp_theory/level1/1_3.php
Проверяйте не только открытие статьи по прямой ссылке. К ней должен существовать путь из раздела, оглавления или соседних глав. В нашем случае прежние курсы остались доступны через библиотеку руководств, а новые материалы собираются в общий каталог.
6. Настройте сервер для прежних расширений
Локальный сервер предварительного просмотра уже знал, что .php содержит HTML. На хостинге это пришлось настроить отдельно. Иначе сервер мог попытаться выполнить файл как PHP или отдать его с неверным типом.
На Beget мы проверили следующую конфигурацию Apache в .htaccess:
Options -Indexes
AcceptPathInfo Off
DirectoryIndex index.html
<IfModule mod_mime.c>
RemoveHandler .php .phtml .php3 .php4 .php5 .php7 .php8
RemoveType .php .phtml
AddType text/html .php
AddType application/manifest+json .webmanifest
</IfModule>
<FilesMatch "\.php$">
SetHandler none
ForceType text/html
</FilesMatch>
AddDefaultCharset UTF-8
ErrorDocument 404 /404.html
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^404/404\.php$ - [R=404,L]
</IfModule>
Эта настройка относится к сайту, где .php-файлы уже содержат готовый HTML. Не применяйте её к папке с действующим PHP-приложением: она отключает исполнение PHP для этих файлов. Конфигурация Nginx задаётся другим способом; доступность директив Apache зависит от хостинга.
AcceptPathInfo Off помогает не выдавать одну статью по ложным адресам вроде /7_1.php/anything. Отдельное правило сохраняет HTTP 404 у прежнего адреса страницы ошибки. Для остальных отсутствующих страниц сервер использует готовую 404.html.
7. Проверьте сборку до публикации
В нашем репозитории используются команды:
make build
make test
make check-site
Они собирают сайт, запускают тесты и проверяют готовые страницы: адреса, заголовки, canonical, sitemap, внутренние ресурсы и переходы. Если сохранены исходные HTML-ответы, доступна дополнительная проверка:
make check
Она сравнивает содержимое перенесённых страниц с оригиналами. Пагинация проверяется отдельно, поскольку мы намеренно её исправили. Изменение главной страницы тоже было согласованной частью работы.
В вашем проекте названия команд могут отличаться. Нужен тот же результат: проверяемый список старых адресов, отсутствие случайно пропавших текстов и ресурсов, рабочие ссылки между материалами. Успешная локальная сборка ещё не подтверждает правильную настройку хостинга.
8. Разверните отдельную тестовую копию
Перед заменой рабочего сайта мы загрузили сборку в отдельную корневую папку тестового сайта. Это важно для ссылок вроде /css/site.css: размещение сборки в подпапке рабочего домена может проверять другой набор ресурсов.
Тестовое окружение нужно защищать от нежелательного доступа и появления дубликатов в поиске. Для закрытых данных используйте аутентификацию. У нашей технической копии были Disallow: / в robots.txt и заголовок X-Robots-Tag: noindex, nofollow, но эти настройки не делают сайт приватным.
Есть существенная тонкость: запрет обхода в robots.txt может помешать роботу увидеть noindex. Если публичная тестовая страница уже известна поиску, одного сочетания этих настроек недостаточно для гарантированного удаления. Для обработки noindex робот должен иметь возможность получить страницу. Это описано в документации Google о noindex.
Тестовые запреты храните отдельно от производственных настроек. Перед публикацией проверьте, что на основном сайте нет случайного Disallow: /, noindex в HTML или заголовка X-Robots-Tag: noindex у статей.
9. Проверьте HTTP, canonical и sitemap
Проверяйте настоящий GET-ответ сервера, а не только картинку в браузере. Например, эта команда получает статью, сохраняет заголовки и показывает итоговый статус:
curl --silent --show-error \
--dump-header /tmp/professorweb-article.headers \
--output /tmp/professorweb-article.html \
--write-out 'HTTP %{http_code}\n' \
'https://professorweb.ru/my/LINQ/linq_xml/level7/7_1.php'
Для этого адреса ожидаем HTTP 200, Content-Type: text/html с кодировкой UTF-8 и текст нужной статьи. В сохранённом HTML должен быть один canonical:
<link rel="canonical"
href="https://professorweb.ru/my/LINQ/linq_xml/level7/7_1.php">
Canonical указывает предпочтительный адрес для поисковой системы, но не перенаправляет посетителя. Согласуйте его с внутренними ссылками и sitemap; сведения о сигнале canonical приведены в документации Google.
Проверьте также две отрицательные ситуации:
curl --silent --show-error --output /dev/null \
--write-out 'HTTP %{http_code}\n' \
'https://professorweb.ru/404/404.php'
curl --silent --show-error --output /dev/null \
--write-out 'HTTP %{http_code}\n' \
'https://professorweb.ru/migration-check-page-that-does-not-exist'
В обоих случаях ожидается HTTP 404. Текст «страница не найдена» вместе с HTTP 200 — ошибочный результат такой проверки.
Далее откройте sitemap.xml. Адреса статей должны соответствовать canonical, не вести на тестовый домен и возвращать ожидаемый успешный ответ. Странице ошибки и служебному поиску не место в карте индексируемых материалов. Сам sitemap помогает обнаруживать страницы, но не гарантирует их индексацию.
Для большого сайта автоматизируйте проверки по реестру, а не ограничивайтесь несколькими выбранными страницами. При первой публикации ProfessorWeb мы получили такой контрольный срез:
| Проверка | Результат |
|---|---|
| Перенесённые страницы | 1711 |
| Защищённые прежние адреса страниц и ресурсов | 1715 |
| Адреса в sitemap первой сборки | 1873 |
| HTTP-запросы при проверке публикации | 1737 |
| Неожиданные ошибки этой проверки | 0 |
Ожидаемые ответы 404 считались правильным результатом, а не ошибкой. Публичные ответы сверялись со статической сборкой. Статусы и совпадение HTML дополнили проверкой поиска и навигации в браузере.
10. Переключите сайт, сохранив возможность отката
Мы сохранили исходную папку рабочего сайта рядом с новой версией, вне её публичного корня. На Beget в рабочую public_html попало только содержимое dist/, включая скрытый .htaccess. Markdown, Git, выгрузки статистики и резервные архивы туда не загружались.
До переключения определите процедуру возврата: где лежит прежняя версия, как вернуть её в рабочую папку, какие страницы и функции проверить после этого. По возможности отрепетируйте процедуру в тестовом окружении. В нашем случае исходная папка и архив были сохранены, но реальный откат рабочей публикации не выполнялся.
После переключения повторите HTTP-проверки уже на основном домене. Проверьте также CSS, JavaScript, favicon и кеширование. У нас после публикации потребовалась отдельная поправка MIME-типа .webmanifest: файл должен возвращаться как application/manifest+json.
Если браузер держит старый CSS, обновлённый серверный файл не обязательно сразу изменит открытую страницу. Для проверки используйте загрузку без кеша, а в регулярном процессе публикации предусмотрите версионирование адресов ресурсов.
Когда всё-таки нужны перенаправления
Мы не меняли адреса статей, поэтому их старые URL напрямую возвращают HTTP 200. Если вы действительно меняете адрес, подготовьте соответствие «старая страница → её новая версия» и постоянное серверное перенаправление, например 301.
Не отправляйте все исчезнувшие статьи на главную: посетитель ожидает конкретный материал. Проверяйте конечную страницу и избегайте цепочек перенаправлений. Такой подход описан в рекомендациях Google по переезду с изменением URL.
В нашем кейсе подтверждён отдельный 301 с HTTP на HTTPS. Вариант домена с www доступен с canonical на основной домен; это не равно перенаправлению www на адрес без www.
Что наблюдать после обновления
Проверка публикации отвечает на вопрос, работают ли адреса и выдаёт ли сервер нужное содержимое. Поведение поиска оценивается после обхода страниц.
Сохраните исходный период для сравнения. Затем следите в Яндекс Вебмастере и Search Console за доступностью важных страниц, ошибками обхода, выбранными canonical, показами и переходами. Сопоставляйте одинаковые группы URL и сравнимые периоды. Если данные ухудшились, сначала проверьте техническую причину: статус, запрет индексации, содержимое ответа, ссылку из оглавления и доступность ресурсов.
Яндекс рекомендует учитывать адреса и перенаправления при изменении структуры сайта. После устранения технических ошибок продолжайте проверять актуальность самих материалов и удобство каталога.
Проверочный список для своей миграции
- Сохранены исходные файлы, конфигурация и необходимые базы данных; предусмотрено восстановление.
- Реестр адресов собран из нескольких источников, включая страницы с поисковыми переходами.
- Каждому прежнему адресу назначен ожидаемый результат: статья, перенаправление или корректная ошибка.
permalinkне зависит от имени Markdown-файла и расположения статьи в каталоге.- Из оглавлений и навигации можно дойти до материалов; соседние главы связаны правильно.
- Хостинг отдаёт старые расширения с нужным MIME-типом и не выполняет статический HTML как PHP.
- Тексты, таблицы, примеры и ресурсы сверены с оригиналами.
- Проверена отдельная тестовая публикация, затем основной домен.
- На статьях основного сайта нет тестового
noindex; canonical и sitemap согласованы. - Отсутствующая страница возвращает настоящий HTTP 404.
- Сохранена прежняя версия и подготовлена процедура отката.
- После публикации ведётся наблюдение за обходом и поисковыми переходами.
Другие материалы по этой теме будут появляться в разделе SEO и поисковые инструменты, ветке миграции сайта и проектном маршруте обновления ProfessorWeb.