Копия сайта целиком нужна чаще, чем кажется: перенос на новый сервер, локальная проверка после редизайна, архив перед крупной миграцией. Качать страницы по одной невозможно, а wget умеет пройти весь ресурс сам, соблюдая структуру каталогов и не теряя ни картинки, ни стили. Режим зеркалирования включается одной опцией --mirror, и дальше всё решает настройка: границы рекурсии, паузы между запросами и правила robots.txt.

Разберём, из чего складывается --mirror, как wget читает robots.txt по умолчанию, зачем существует опция robots=off при зеркалировании собственных ресурсов и какие настройки превращают первую выгрузку в повторяемый процесс.

Из чего складывается режим зеркалирования

Сокращение --mirror разворачивается в связку из четырёх режимов: рекурсивная загрузка -r, бесконечная глубина -l inf, отметки времени -N и --no-remove-listing для служебных файлов FTP. Рекурсия заставляет wget идти по ссылкам со стартовой страницы, отметки времени позволяют при повторном запуске скачивать только изменённые файлы, а бесконечная глубина снимает искусственные ограничения на число уровней каталогов. Для понимания масштаба: обычная рекурсия -r ограничена пятью уровнями, и сайты с глубокой структурой без -l inf обрезаются. Те же четыре режима можно набрать руками: wget -r -N -l inf --no-remove-listing даст то же самое, и длинная форма полезна для понимания, что именно делает сокращение.

wget --mirror -P /srv/mirror https://www.example.com/

Опция -P задаёт корневой каталог копии, и wget воссоздаёт внутри него структуру домена целиком, каталог за каталогом. Без -P файлы упадут в текущую директорию, и через час работы в ней будет трудно найти что-то кроме страниц. Для FTP-зеркал --no-remove-listing сохраняет файлы .listing со списками каталогов, что ускоряет повторные проходы: wget сверяет сохранённые списки с сервером и не запрашивает всё заново. Структуру каталогов подправляют флаги -nH и --protocol-directories: первый не создаёт каталог с именем домена и кладёт содержимое прямо в корень копии, второй раскладывает файлы по протоколам, когда в одном каталоге смешаны http и ftp. Для одностраничных копий уместен -x, который строит каталоги даже вокруг одиночного файла.

Границы рекурсии и защита от расползания

Бесконечная глубина хороша ровно до тех пор, пока ссылки ведут внутрь сайта. Одна внешняя кнопка, и wget уползает скачивать полинтернета. Страховка собирается из нескольких флагов: --no-parent запрещает подниматься выше стартового каталога, -H разрешает следовать на внешние хосты только вместе со списком -D, а --follow-ftp включает переходы по FTP-ссылкам, если они встречаются на страницах.

wget --mirror --no-parent -H \
  -D www.example.com,static.example.com \
  --reject "*.zip,*.mp4" \
  -P /srv/mirror https://www.example.com/docs/

Паттерны --accept и --reject отсекают тяжёлые файлы, которые в копии документации не нужны. Список --exclude-directories пропускает служебные разделы вроде /cgi-bin/ или черновиков, а --cut-dirs убирает лишние уровни из локальных путей, когда структура сервера не совпадает с желаемой. Точнее границу рисуют флаги --follow-tags и --ignore-tags: первый ограничивает набор тегов, по которым wget идёт вглубь, второй исключает теги из разбора, например навигационные ссылки. Разумный порядок такой: сначала короткий пробный проход с -l 3 и осмотр результата, потом снятие ограничений для полного зеркала. Так поломанная ссылка или зацикленный генератор страниц обнаруживаются до того, как размножатся на терабайт. Смежные хосты вроде CDN со стилями включают в -D, но нередко проще прогнать зеркало отдельно для каждого домена в общий каталог -P: границы честнее, а журналы читаются лучше. Список --include-directories работает зеркально к --exclude-directories и сужает проход до выбранных разделов, что выручает при частичном зеркалировании крупного портала. Шумный рекламный хост убирает --exclude-domains, и список -D остаётся собранным из содержательных узлов.

Чтение robots.txt по умолчанию и его влияние

По умолчанию wget уважает стандарт Robot Exclusion: перед началом работы с каждым хостом она запрашивает файл /robots.txt в корне и сверяет с ним каждую будущую загрузку. В файле перечислены правила для ботов: строки User-agent называют программы, строки Disallow перечисляют закрытые пути.

User-agent: wget
Disallow: /staging/

User-agent: *
Disallow: /cgi-bin/
Disallow: /tmp/

wget сначала ищет блок со своим именем, затем общий блок со звёздочкой, и пути из Disallow пропускает, фиксируя пропуск в журнале. Правила сверяются по префиксу пути, и адрес пропускается, если запрет стоит на любой из его родительских каталогов. Для обычного копирования это желаемое поведение: сервер явно просит ботов не ходить в отдельные разделы, и wget его слушается. Если стартовая страница закрыта правилом целиком, зеркало не получится вовсе, и в логе останутся одни отказы. Файл читается при каждом старте, и изменения правил на стороне сервера подхватываются без перенастройки wget. Быструю проверку адресов без сохранения файлов делает режим --spider: wget проходит по ссылкам и только помечает, что доступно, а что нет. Сухой прогон с --spider перед настоящим зеркалом показывает объём работы и битые ссылки, не записывая на диск ни байта.

Опция robots=off для собственных ресурсов

У зеркалирования собственного сайта есть особый случай: robots.txt может запрещать всё подряд, потому что писался для поисковых роботов, а не для владельца ресурса. Внутренняя вики с правилом Disallow: / честно закрывает wget на первой же странице. Для такого сценария существует настройка -e robots=off: имя директивы конфигурации robots, значение off отключает проверку файла, а ключ -e, он же --execute, выполняет директиву прямо из командной строки, без правки системного /etc/wgetrc. По умолчанию wget работает с robots=on.

# копия собственного ресурса для локального стенда
wget --mirror --convert-links -e robots=off \
  -P /srv/mirror https://www.example.com/

Применять настройку стоит по тем же правилам, по которым хранят ключи от сервера: только для ресурсов, которыми распоряжаетесь вы или ваша команда. Порядок действий выглядит уважительным: сначала посмотреть robots.txt обычным curl и убедиться, что речь о вашем сайте, затем запускать зеркало с отключённой проверкой и записать флаг в скрипт с комментарием, объясняющим, чей это ресурс. Исключения должны быть осознанными и видимыми в коде, тогда проверка robots.txt остаётся защитой по умолчанию, а не помехой.

Для стенда на staging-поддомене владелец обычно пишет общий запрет всему сайту, чтобы поисковые роботы не индексировали черновики, и именно такой файл мешает зеркалу собственного ресурса. Проверка перед запуском занимает минуту: просмотр robots.txt, взгляд на правила, и решение о флаге в скрипте принимается осознанно, а не по догадке.

Ключ -e не единственный способ передать директиву: wget --config=mirror.conf читает настройки из файлового конфига рядом со скриптом, и строка robots = off лежит там с тем же комментарием о владельце ресурса. Проектный конфиг удобен, когда зеркалят несколько доменов с разными правилами: по файлу на каждый профиль.

Паузы, скорость и вежливость к серверу

Зеркалирование среднего сайта даёт сотни запросов за минуты, и без ограничений wget создаёт нагрузку, которую владельцы ресурса не оценят. Опция --wait=1 задаёт паузу в секундах между запросами, --random-wait добавляет к ней разброс от половины до полутора пауз, чтобы запросы не шли метрономом. Скорость канала ограничивает --limit-rate=2m, число попыток на файл - --tries=20, а --waitretry сглаживает паузы между повторами. Суффиксы у --limit-rate понимают k, m и g: 500k это полмегабайта в секунду, и значение подбирают под часы, когда сервер наименее занят. Общий --timeout=30 накрывает и соединение, и чтение, а --connect-timeout и --read-timeout разделяют эти пределы для неспешных серверов.

wget --mirror --wait=1 --random-wait --limit-rate=2m \
  --tries=20 --timeout=30 --no-verbose \
  -P /srv/mirror https://www.example.com/

Для собственного сервера паузы можно сокращать, для чужих ресурсов хорошая практика начинается с --wait и журнала, в котором виден темп. Режим --no-verbose оставляет в логе одну строку на загрузку, и итоговый файл читается глазами, не разваливаясь на тысячи строк с адресами. Тем же целям служит --quiet для полностью немых прогонов в скриптах, где о важном wget сообщит ненулевым кодом выхода. Нестабильные моменты выравнивают флаги повторов: --retry-connrefused дожидается подъёма сервера после рестарта, что полезно при копировании сразу после деплоя, а --waitretry=10 наращивает паузу с каждым отказом. Число попыток --tries=0 означает терпение без границ, и для ночных прогонов это разумный предел.

Ссылки, имена файлов и повторное обновление

Копия, которая открывается, отличается от копии, которая просто скачана. Флаг --convert-links переписывает абсолютные адреса на относительные, --page-requisites добирает стили, скрипты и картинки, без которых страницы выглядят ободранно, а --adjust-extension даёт динамическим страницам расширение .html, чтобы локальный сервер понимал, чем их отдавать.

wget --mirror --convert-links --adjust-extension \
  --page-requisites --no-parent \
  -P /srv/mirror https://www.example.com/

Имена файлов на Windows-серверах выправляет --restrict-file-names=windows, а -x принудительно строит каталоги даже для однофайловых загрузок. Реквизиты страниц на CDN требуют -H и -D в комплекте: без внешних хостов стили останутся на сервере, и копия потеряет оформление. Повторное обновление зеркала сводится к тому же самому запуску: отметки времени -N сравнивают даты, и скачиваются только изменившиеся страницы. Для еженедельной копии этого достаточно, а полноценные системы резервного копирования начинаются там, где нужна история версий и мгновенное восстановление.

Скрипт еженедельной копии и контроль результата

Настройки удобнее держать в скрипте, а не в истории команд. Порядок действий при обновлении зеркала выглядит так:

  1. проверить доступность сайта и содержимое его robots.txt;
  2. запустить wget с --mirror, паузами и отключённой проверкой robots для своего ресурса;
  3. дождаться завершения и просмотреть журнал на предмет отказов и редиректов;
  4. сверить число файлов и объём копии с прошлым прогоном;
  5. зафиксировать дату копии в имени каталога и убрать устаревшие версии.
#!/usr/bin/env bash
set -euo pipefail
dst=/srv/mirror/$(date +%F)
wget --mirror --convert-links --adjust-extension \
  --page-requisites --no-parent --wait=1 --random-wait \
  -e robots=off -P "$dst" https://www.example.com/ \
  > "$dst.log" 2>&1
echo "файлов: $(find "$dst" -type f | wc -l)"
echo "объём: $(du -sh "$dst" | cut -f1)"

Каталог "$dst" создаётся wget вместе с копией, а лог рядом позволяет за минуту понять, что именно не скачалось. Сравнение объёма с прошлым прогоном отлавливает обе крайности: пропавшую половину сайта после неудачного деплоя и внезапно разросшиеся каталоги, которые кто-то забыл почистить. Расписание удобно держать в systemd-таймере с OnCalendar и небольшим RandomizedDelaySec, чтобы запуск не совпадал с ночными задачами. Хранение нескольких последних копий с ротацией find -mtime +21 -delete по каталогу зеркал освобождает место без ручной чистки, а имена по дате делают выбор нужной версии очевидным. Зеркало wget не заменяет полноценный бэкап с историей, но как быстрая и предсказуемая копия ресурса для переноса или локальной работы остаётся инструментом первого выбора уже много лет. Разницу между копиями показывает diff -r по двум датированным каталогам: он же ловит страницы, пропавшие с сервера, до того, как это заметят посетители. Через месяц таких прогонов копия перестаёт быть просто архивом и превращается в инструмент: по ней видна динамика сайта, растёт ли контент, зачищаются ли старые разделы.