Технический SEO-аудит не обязательно начинать с дорогого краулера или сложной аналитической платформы. Многие важные параметры сайта можно проверить средствами Linux: выяснить, какие HTTP-ответы получает поисковый робот, найти лишние перенаправления, проверить директивы индексации, изучить серверные журналы и измерить время ответа.
В этой статье разберём последовательный подход к техническому аудиту сайта с помощью командной строки. Он подойдёт владельцам сайтов, администраторам серверов, веб-разработчикам и SEO-специалистам, которым важно не просто увидеть ошибку, но и понять её причину.
В качестве основных инструментов используются curl, dig, grep, awk, zcat и Python 3. Для анализа HTML в автоматизированной проверке применяется встроенный модуль html.parser: он надёжнее регулярных выражений и не требует установки сторонних библиотек.
Что может и чего не может технический SEO-аудит из командной строки
Командная строка позволяет проверить технические сигналы, которые сервер отдаёт при запросе страницы. Это полезно для диагностики доступности сайта, редиректов, метатегов, HTTP-заголовков, DNS и серверных ошибок.
Однако такой аудит не равнозначен полноценному сканированию сайта. Простой HTTP-клиент не выполняет JavaScript, не оценивает качество контента и не определяет позиции страниц в поисковой выдаче. Он также не измеряет пользовательские Core Web Vitals так, как это делают браузерные инструменты и системы сбора полевых данных.
Важно различать три уровня проверки:
- Технический ответ сервера. Код HTTP, заголовки, редиректы, полученный HTML и время сетевого запроса.
- Состояние страницы. Title, Description, H1, canonical, robots, ссылки и другие элементы исходного HTML.
- Фактическое состояние в поисковой системе. Результат обработки страницы роботом, выбранный поисковиком canonical, наличие страницы в индексе и её видимость в поиске.
Эти уровни связаны, но не заменяют друг друга. Например, ответ 200 OK означает успешное выполнение HTTP-запроса, но сам по себе не подтверждает, что страница индексируется или должна индексироваться.
1. Подготовка Linux и базовая диагностика HTTP
1.1. Установка необходимых инструментов
В Ubuntu и Debian установите базовый набор утилит:
sudo apt update sudo apt install curl dnsutils grep gawk sed coreutils python3
curl отправляет HTTP-запросы, dig и host проверяют DNS, а стандартные утилиты Linux помогают фильтровать и анализировать результаты. Python 3 понадобится для разбора HTML и формирования отчёта.
1.2. Проверка заголовков ответа
curl -I https://example.com/
Ключ -I отправляет запрос методом HEAD и выводит заголовки без тела страницы. В ответе полезно посмотреть:
- строку состояния, например
HTTP/2 200; Content-Type— тип возвращаемого содержимого;Location— адрес назначения перенаправления;Cache-Control,Expires,ETag— параметры кэширования;X*-Robots-Tag— директивы для поисковых роботов.
Учитывайте, что некоторые серверы обрабатывают HEAD и GET по-разному. Если результат выглядит подозрительно, выполните обычный GET-запрос.
1.3. Проверка обычного GET-запроса
curl -sS -D - -o /dev/null https://example.com/
Команда отправляет GET, выводит заголовки ответа и не сохраняет тело документа. Это более показательная проверка, когда нужно увидеть ответ на обычное обращение к странице.
1.4. Сохранение HTML для изучения
curl -sS -L --max-time 25 https://example.com/ -o page.html grep -i '<title' page.html grep -i 'name="description"' page.html grep -i 'rel="canonical"' page.html
Так можно быстро найти основные элементы исходного HTML. Но grep не является HTML-парсером: он может пропустить тег с необычным порядком атрибутов, переносами строк или одинарными кавычками. Для автоматической проверки большого числа страниц лучше использовать парсер.
1.5. Проверка цепочки перенаправлений
curl -sSIL --max-redirs 10 --max-time 25 http://example.com/
В выводе будут видны последовательные HTTP-ответы и заголовки Location. Например:
HTTP/1.1 301 Moved Permanently Location: https://www.example.com/ HTTP/2 200
В этом примере запрос перенаправляется один раз и завершается успешным ответом. Если переходов несколько, стоит проверить правила веб-сервера, CDN, CMS и настройки предпочтительного адреса сайта.
1.6. Получение итогового URL и времени запроса
curl -sS -L --max-redirs 10 --max-time 25 -o /dev/null \
-w 'HTTP: %{http_code}\nRedirects: %{num_redirects}\nFinal URL: %{url_effective}\nTotal: %{time_total}s\n' \
https://example.com/
Параметр -L разрешает следовать редиректам, а --max-redirs ограничивает их количество. Время time_total показывает продолжительность выполнения запроса с учётом сети и получения ответа. Это не оценка полной скорости отображения страницы в браузере.
1.7. Что curl не видит
curl загружает ответ сервера, но не исполняет JavaScript и не строит полноценную браузерную модель страницы. Если важный текст, ссылки, canonical или метаданные добавляются скриптами после загрузки, простой запрос может показать не всю картину.
Для проверки рендеринга используйте браузерные инструменты: Chrome DevTools, Lighthouse или проверку URL в Google Search Console. При необходимости можно получить отрендеренный DOM через Chromium в headless-режиме:
chromium --headless --no-sandbox --disable-gpu \ --dump-dom https://example.com/
Путь к исполняемому файлу может отличаться в зависимости от дистрибутива. Не запускайте браузер от root без понимания последствий и используйте подходящий режим изоляции.
2. Проверка сканирования и индексации
2.1. Анализ robots.txt
curl -sS -D - https://example.com/robots.txt
Проверьте код ответа и содержимое файла. Типичный пример:
User-agent: * Disallow: /private/ Sitemap: https://example.com/sitemap.xml
Особое внимание уделите широким запретам, например Disallow: /. Такое правило может закрыть сканирование всего сайта для соответствующей группы роботов. Оно часто появляется на тестовых сайтах и иногда забывается при переносе проекта на основной домен.
Robots.txt управляет доступом роботов к URL, но не является надёжным способом удалить страницу из индекса. Если поисковик уже знает адрес, он в некоторых случаях может показать URL без содержимого. Для исключения страницы из индекса применяют корректные директивы индексации и другие подходящие механизмы.
2.2. Проверка sitemap.xml
curl -sS -D - https://example.com/sitemap.xml | head -n 40
Убедитесь, что карта сайта доступна, возвращает XML, а не HTML-страницу ошибки, и содержит актуальные URL. В карту обычно включают канонические страницы, которые должны индексироваться и возвращают успешный ответ.
Если используется индекс sitemap, он может ссылаться на несколько отдельных карт. Для надёжной обработки больших или вложенных карт лучше использовать XML-парсер, а не извлекать адреса регулярными выражениями.
2.3. Проверка Meta* Robots
grep -iE '<meta*[^>]+name=["'\"'\"']robots' page.html
Пример директивы:
<meta* name="robots" content="noindex,follow">
noindex запрещает индексировать страницу для поддерживающих эту директиву поисковых систем, если робот смог получить и обработать страницу. Проверьте, не попала ли такая директива на важные разделы сайта из-за настроек CMS, SEO-плагина или шаблона.
2.4. Проверка X*-Robots-Tag
curl -sSI https://example.com/ | grep -i '^x*-robots-tag:'
Директивы индексации могут передаваться не только через HTML, но и через HTTP-заголовок X*-Robots-Tag. Это особенно важно для PDF, изображений и других файлов, у которых нет HTML-метатегов.
2.5. Проверка canonical
grep -i 'canonical' page.html
Canonical указывает поисковой системе предпочтительный URL для группы одинаковых или близких по содержанию страниц. Это сигнал, а не безусловная команда: поисковик может выбрать другой адрес.
Проверьте, что canonical ведёт на существующий URL, использует правильный протокол и предпочтительный хост, не содержит случайных параметров и не указывает на страницу с ошибкой, редиректом или запретом индексации.
2.6. Проверка DNS
dig example.com A dig example.com AAAA dig www.example.com A dig example.com NS
DNS-ошибки могут сделать сайт недоступным полностью или частично. Сравните ответы с настройками домена и убедитесь, что записи A, AAAA и CNAME ведут на ожидаемую инфраструктуру. Если сайт работает через CDN, учитывайте, что адреса могут принадлежать CDN, а не исходному серверу.
2.7. Проверка реакции на User-Agent
curl -sS -A 'Googlebot' -o /dev/null -w '%{http_code}\n' https://example.com/
Это только тест реакции сайта на строку User-Agent. Подстановка слова Googlebot не превращает запрос в настоящий запрос поискового робота. Для подтверждения подлинности Googlebot анализируют IP-адрес и выполняют обратную и прямую DNS-проверку по официальной процедуре Google.
3. Аудит списка URL и перенаправлений
3.1. Подготовка списка страниц
Создайте файл urls.txt, указав по одному адресу на строку:
https://example.com/ https://example.com/about/ https://example.com/catalog/ https://example.com/contacts/
В список можно включить URL из sitemap, важных разделов сайта, внутренней навигации и страниц, которые недавно изменялись. Для первого запуска разумно проверить небольшой набор, а затем расширить его.
3.2. Проверка кодов состояния
while IFS= read -r url || [ -n "$url" ]; do
[ -z "$url" ] && continue
case "$url" in \#*) continue ;; esac
curl -sS -L --max-redirs 10 --max-time 25 -o /dev/null \
-w '%{http_code} redirects=%{num_redirects} final=%{url_effective}\n' \
"$url"
done < <(tr -d '\r' < urls.txt)
Ключ -L показывает итоговый ответ после редиректов. Поэтому в выводе будет конечный код, а количество перенаправлений укажет, сколько переходов потребовалось.
3.3. Проверка первого ответа без перехода
curl -sS -o /dev/null -w 'HTTP: %{http_code}\nRedirect: %{redirect_url}\n' \
http://example.com/
Этот запрос не следует за перенаправлением и позволяет увидеть исходный статус и адрес назначения. Такой тест полезен, когда нужно отдельно проверить 301, 302, 307 или 308.
3.4. Проверка четырёх вариантов домена
Проверьте HTTP и HTTPS, а также варианты с www и без него:
for url in \
http://example.com/ \
http://www.example.com/ \
https://example.com/ \
https://www.example.com/
do
curl -sS -L --max-redirs 10 --max-time 25 -o /dev/null \
-w '%{http_code} hops=%{num_redirects} %{url_effective}\n' "$url"
done
Все варианты должны вести к одной согласованной предпочтительной версии сайта. Какой именно вариант выбрать — с www или без него — не принципиально. Важно, чтобы внутренние ссылки, canonical, sitemap и редиректы не противоречили друг другу.
3.5. Проверка несуществующего URL и soft 404
curl -sS -L -o /dev/null -w '%{http_code} %{url_effective}\n' \
https://example.com/page-that-does-not-exist-987654
Для действительно отсутствующей страницы обычно ожидается 404 или 410. Если сервер возвращает 200 с шаблоном ошибки, это может быть soft 404. Перенаправление всех несуществующих URL на главную также не всегда является корректным решением: адрес должен вести на действительно подходящую замену, если она существует.
3.6. Как трактовать коды ответа
- 200 — запрос выполнен успешно. Проверьте, что в ответе действительно нужная страница, а не заглушка.
- 301 и 308 — постоянное перенаправление. Проверьте правильность целевого URL и отсутствие лишних переходов.
- 302 и 307 — временное перенаправление. Убедитесь, что временный характер ответа соответствует задаче.
- 401 и 403 — доступ требует авторизации или запрещён. Проверьте, не блокируются ли важные страницы.
- 404 и 410 — ресурс не найден или удалён. Сверьте результат с назначением URL.
- 429 — слишком много запросов. Уменьшите частоту сканирования и проверьте ограничения сервера.
- 5xx — ошибка сервера или приложения. Изучите журналы и состояние инфраструктуры.
- 000 / ошибка curl — запрос не получил корректного HTTP-ответа. Возможны проблемы DNS, TLS, сети или тайм-аут.
Не каждый редирект или 404 является SEO-ошибкой. Оценивать результат нужно с учётом того, должен ли URL существовать и какую задачу он выполняет.
4. Анализ журналов Nginx и Apache
4.1. Где искать access.log
Типичные пути в Ubuntu и Debian:
- Nginx:
/var/log/nginx/access.log; - Apache:
/var/log/apache2/access.log.
На виртуальном хостинге, в ISPmanager или за прокси журнал может находиться в другом каталоге. Кроме того, формат записи настраивается администратором, поэтому перед использованием awk проверьте конфигурацию логирования.
4.2. Просмотр последних запросов
sudo tail -n 50 /var/log/nginx/access.log
Для поиска ошибок сервера:
sudo grep -E '" [45][0-9][0-9] ' /var/log/nginx/access.log | tail -n 50
Если включена ротация, старые файлы могут иметь суффиксы .1 или .gz. Для чтения обычных и сжатых журналов можно использовать:
sudo zcat -f /var/log/nginx/access.log* | tail -n 100
4.3. Подсчёт HTTP-кодов
sudo awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -nr
Команда подходит для стандартного формата Combined, в котором код ответа находится в девятом поле. Если формат журнала изменён, номер поля может быть другим. Не используйте этот пример без проверки структуры строки.
4.4. Поиск часто запрашиваемых URL
sudo awk '{print $7}' /var/log/nginx/access.log \
| sort | uniq -c | sort -nr | head -20Это помогает увидеть наиболее часто запрашиваемые пути. При анализе учитывайте параметры URL, запросы к статическим файлам, ботов и трафик CDN.
4.5. Анализ запросов поисковых роботов
sudo grep -i 'Googlebot' /var/log/nginx/access.log | tail -n 50
Для подсчёта наиболее часто запрашиваемых путей:
sudo grep -i 'Googlebot' /var/log/nginx/access.log \
| awk '{print $7}' | sort | uniq -c | sort -nr | head -20
Для поиска ошибок в запросах с таким User-Agent:
sudo grep -i 'Googlebot' /var/log/nginx/access.log \
| awk '$9 ~ /^(404|410|5[0-9][0-9])$/ {print $9, $7}' \
| sort | uniq -c | sort -nr | head -30
Фильтрация по User-Agent удобна для предварительного анализа, но не подтверждает подлинность робота: строку можно подделать. Для серьёзных выводов проверяйте IP-адреса по официальной процедуре поисковой системы.
4.6. Проверка подлинности Googlebot
Для проверки конкретного IP используется обратный DNS-запрос, а затем прямой запрос полученного имени. Имя должно принадлежать официальному домену, указанному Google для соответствующего типа робота, а прямое разрешение должно вернуть исходный IP. Сверяйтесь с актуальной документацией Google: набор доменов и процедуры проверки могут различаться для разных типов роботов.
Не публикуйте необработанные журналы без необходимости. В них могут содержаться IP-адреса, URL с идентификаторами, параметры запросов и другие сведения о посетителях. Ограничивайте доступ, срок хранения и состав передаваемых данных.
5. Время ответа, кэширование и сжатие
5.1. Измерение этапов HTTP-запроса
curl -sS -o /dev/null \
-w 'DNS: %{time_namelookup}s\nConnect: %{time_connect}s\nTLS: %{time_appconnect}s\nFirst byte: %{time_starttransfer}s\nTotal: %{time_total}s\n' \
https://example.com/
Показатели помогают понять, на каком этапе возникает задержка: разрешение DNS, подключение, TLS или ожидание первого байта. На результат влияют расположение проверяющего компьютера, сеть, CDN, состояние кэша и текущая нагрузка.
Проведите несколько измерений, а не делайте вывод по одному запросу:
for i in 1 2 3 4 5; do
curl -sS -o /dev/null -w '%{time_starttransfer}\n' https://example.com/
done5.2. Проверка заголовков кэширования
curl -sSI https://example.com/ \ | grep -iE '^(cache-control|expires|age|etag|last-modified):'
Политика кэширования зависит от типа ресурса. HTML-документ, изображение, CSS и JavaScript могут иметь разные сроки хранения. Длительное кэширование удобно для версионированных статических файлов, но опасно для ресурсов, содержимое которых меняется без изменения URL.
5.3. Проверка сжатия
curl -sS -D - -o /dev/null \ -H 'Accept-Encoding: gzip, br' https://example.com/ \ | grep -iE '^(content-encoding|vary):'
Если сервер поддерживает сжатие для данного типа содержимого, в ответе может присутствовать Content-Encoding: gzip или Content-Encoding: br. Отсутствие этого заголовка не всегда означает ошибку: изображения и другие уже сжатые форматы обычно не нуждаются в дополнительном сжатии.
5.4. Core Web Vitals и ограничения curl
Командная строка не измеряет пользовательские показатели Core Web Vitals. Для оценки взаимодействия с сайтом применяются:
- LCP — время отображения крупнейшего элемента контента;
- INP — задержка взаимодействия пользователя со страницей;
- CLS — визуальная нестабильность макета.
Ориентиры Google для хорошего пользовательского опыта: LCP не более 2,5 секунды, INP не более 200 миллисекунд, CLS не более 0,1. Эти значения оцениваются по 75-му процентилю полевых данных. Один запрос curl не позволяет подтвердить соответствие сайта этим порогам.
Для лабораторных измерений используйте Lighthouse и PageSpeed Insights, а для реальной картины — полевые данные Chrome UX Report и доступные отчёты поисковых систем.
6. Автоматизация технического SEO-аудита
6.1. Почему для HTML лучше использовать Python
Bash отлично подходит для последовательного запуска команд и обработки файлов, но регулярные выражения неудобны для разбора HTML. Порядок атрибутов, кавычки, переносы строк и вложенные элементы могут привести к пропущенным данным.
Ниже используется Python 3 и встроенный HTML-парсер. Скрипт не требует BeautifulSoup, Requests или других сторонних пакетов: HTTP-запросы выполняются через стандартный модуль urllib, а HTML разбирается с помощью html.parser.
6.2. Создание файла seo-audit.py
Создайте файл seo-audit.py и вставьте в него следующий код. Комментарии в скрипте поясняют назначение основных операций.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Базовый технический SEO-аудит списка URL.
Использует только стандартную библиотеку Python 3.
Запуск:
python3 seo-audit.py urls.txt seo-report.csv
Дополнительные параметры:
DELAY=1 пауза между запросами в секундах
TIMEOUT=25 тайм-аут одного запроса
MAX_BYTES=5000000 максимальный объём загружаемого HTML
"""
import csv
import os
import sys
import time
import urllib.request
import urllib.error
from html.parser import HTMLParser
from urllib.parse import urlparse
# ---------- Настройки ----------
DELAY = float(os.getenv("DELAY", "1"))
TIMEOUT = float(os.getenv("TIMEOUT", "25"))
MAX_BYTES = int(os.getenv("MAX_BYTES", "5000000"))
USER_AGENT = "TechSEOAudit/2.0 (+https://example.com/seo-audit-info)"
# ---------- Разбор HTML ----------
class PageParser(HTMLParser):
"""Извлекает основные SEO-элементы из исходного HTML."""
def __init__(self):
super().__init__(convert_charrefs=True)
self.title = ""
self.in_title = False
self.description = ""
self.robots = ""
self.canonical = ""
self.h1 = []
self.in_h1 = False
self.current_h1 = []
def handle_starttag(self, tag, attrs):
attrs = dict(attrs)
tag = tag.lower()
if tag == "title":
self.in_title = True
elif tag == "h1":
self.in_h1 = True
self.current_h1 = []
elif tag == "meta*":
name = attrs.get("name", "").strip().lower()
content = attrs.get("content", "").strip()
if name == "description":
self.description = content
elif name == "robots":
self.robots = content
elif tag == "link":
rel = attrs.get("rel", "").lower().split()
if "canonical" in rel:
self.canonical = attrs.get("href", "").strip()
def handle_endtag(self, tag):
tag = tag.lower()
if tag == "title":
self.in_title = False
elif tag == "h1" and self.in_h1:
value = " ".join("".join(self.current_h1).split())
if value:
self.h1.append(value)
self.in_h1 = False
self.current_h1 = []
def handle_data(self, data):
if self.in_title:
self.title += data
if self.in_h1:
self.current_h1.append(data)
# ---------- Проверка одного URL ----------
def audit_url(url):
"""Запрашивает страницу и возвращает словарь с результатами."""
result = {
"input_url": url,
"http_status": "",
"redirects": "",
"final_url": "",
"seconds": "",
"content_type": "",
"html_bytes": "",
"title": "",
"title_length": "",
"description": "",
"description_length": "",
"h1_count": "",
"h1": "",
"canonical": "",
"meta_robots": "",
"x_robots_tag": "",
"issues": "",
"error": "",
}
request = urllib.request.Request(
url,
headers={"User-Agent": USER_AGENT},
method="GET"
)
started = time.monotonic()
try:
with urllib.request.urlopen(request, timeout=TIMEOUT) as response:
status = response.status
final_url = response.geturl()
headers = response.headers
# Читаем ограниченный объём данных, чтобы не загружать
# огромные документы и случайные бинарные файлы целиком.
body = response.read(MAX_BYTES + 1)
if len(body) > MAX_BYTES:
body = body[:MAX_BYTES]
result["issues"] = "HTML_TRUNCATED"
result["http_status"] = status
result["final_url"] = final_url
result["content_type"] = headers.get("Content-Type", "")
result["x_robots_tag"] = headers.get("X*-Robots-Tag", "")
result["html_bytes"] = len(body)
# urllib автоматически следует за обычными редиректами,
# но не предоставляет здесь надёжное число переходов.
# Поэтому число редиректов намеренно не подменяется догадкой.
result["redirects"] = "FOLLOWED"
content_type = result["content_type"].lower()
if "text/html" in content_type or not content_type:
charset = headers.get_content_charset() or "utf-8"
html = body.decode(charset, errors="replace")
parser = PageParser()
parser.feed(html)
title = " ".join(parser.title.split())
description = parser.description
h1_values = parser.h1
result["title"] = title
result["title_length"] = len(title)
result["description"] = description
result["description_length"] = len(description)
result["h1_count"] = len(h1_values)
result["h1"] = " | ".join(h1_values)
result["canonical"] = parser.canonical
result["meta_robots"] = parser.robots
issues = []
if not title:
issues.append("TITLE_MISSING")
if not description:
issues.append("DESCRIPTION_MISSING")
if not h1_values:
issues.append("H1_MISSING")
elif len(h1_values) > 1:
issues.append("MULTIPLE_H1")
if "noindex" in parser.robots.lower():
issues.append("META_NOINDEX")
if "noindex" in result["x_robots_tag"].lower():
issues.append("HEADER_NOINDEX")
if not parser.canonical:
issues.append("CANONICAL_MISSING")
if result["issues"]:
issues.insert(0, result["issues"])
result["issues"] = ";".join(issues)
except urllib.error.HTTPError as error:
result["http_status"] = error.code
result["final_url"] = error.geturl()
result["x_robots_tag"] = error.headers.get("X*-Robots-Tag", "")
result["error"] = str(error)
except Exception as error:
result["error"] = f"{type(error).__name__}: {error}"
result["seconds"] = round(time.monotonic() - started, 3)
return result
# ---------- Запуск аудита и экспорт CSV ----------
def main():
input_file = sys.argv[1] if len(sys.argv) > 1 else "urls.txt"
output_file = sys.argv[2] if len(sys.argv) > 2 else "seo-report.csv"
if not os.path.isfile(input_file):
print(f"Не найден входной файл: {input_file}", file=sys.stderr)
return 1
# Убираем пустые строки и комментарии, сохраняя порядок URL.
with open(input_file, "r", encoding="utf-8-sig") as source:
urls = [
line.strip()
for line in source
if line.strip() and not line.lstrip().startswith("#")
]
fields = [
"input_url", "http_status", "redirects", "final_url", "seconds",
"content_type", "html_bytes", "title", "title_length",
"description", "description_length", "h1_count", "h1",
"canonical", "meta_robots", "x_robots_tag", "issues", "error"
]
checked = 0
errors = 0
# utf-8-sig добавляет BOM, чтобы Excel корректно распознавал UTF-8.
with open(output_file, "w", newline="", encoding="utf-8-sig") as report:
writer = csv.DictWriter(report, fieldnames=fields)
writer.writeheader()
for url in urls:
parsed = urlparse(url)
if parsed.scheme not in ("http", "https") or not parsed.netloc:
row = {field: "" for field in fields}
row["input_url"] = url
row["error"] = "Некорректный URL: требуется http:// или https://"
else:
print(f"[{checked + 1}/{len(urls)}] Проверка: {url}")
row = audit_url(url)
writer.writerow(row)
checked += 1
if row.get("error"):
errors += 1
time.sleep(max(0, DELAY))
print()
print("Аудит завершён.")
print(f"Проверено URL: {checked}")
print(f"Ошибок запросов или URL: {errors}")
print(f"Отчёт: {output_file}")
return 0
if __name__ == "__main__":
raise SystemExit(main())6.3. Запуск скрипта
Создайте список URL в файле urls.txt, затем выполните:
python3 seo-audit.py urls.txt seo-report.csv
Чтобы уменьшить частоту запросов и увеличить тайм-аут:
DELAY=2 TIMEOUT=40 python3 seo-audit.py urls.txt seo-report.csv
Скрипт создаёт CSV в UTF-8 с BOM, поэтому русские значения должны корректно открываться в современных версиях Excel. В отчёт попадают исходный и конечный URL, статус, тип содержимого, размер полученного HTML, Title, Description, H1, Canonical, директивы robots и обнаруженные предупреждения.
6.4. Важные ограничения автоматического скрипта
Это облегчённый проверяющий инструмент, а не полноценный краулер. В частности:
- Он проверяет только URL, заранее записанные в файл, и не обходит сайт по внутренним ссылкам.
- Он не исполняет JavaScript и анализирует исходный HTML.
- Для экономии ресурсов размер загружаемого тела ограничен переменной MAX_BYTES.
- В этой версии число редиректов обозначается как FOLLOWED, а не вычисляется приблизительно. Для точного подсчёта цепочки нужен отдельный обработчик переходов.
- Дубли Title и Description можно определить после выгрузки отчёта, сгруппировав строки по соответствующим столбцам.
- Наличие canonical и robots оценивается по HTML и заголовкам, но окончательное решение об индексации принимает поисковая система.
Не запускайте массовый аудит без ограничений частоты. Для крупного сайта увеличьте задержку, проверяйте сайт частями и не обходите страницы, на которые у вас нет разрешения.
7. Интерпретация результатов и исправление ошибок
7.1. Как читать отчёт
В первую очередь отфильтруйте строки с ошибками запроса и неожиданными HTTP-статусами. Затем проверьте страницы с отсутствующими Title, Description, H1 или Canonical. Отдельно изучите директивы noindex и несоответствие canonical конечному URL.
Наличие предупреждения не всегда означает ошибку. Например, отсутствие meta* description не блокирует индексацию, а несколько H1 сами по себе не являются автоматическим основанием для санкций. Эти сигналы помогают найти страницы, которые требуют ручной оценки.
7.2. Типовые симптомы и направления проверки
| Симптом | Что проверить | Возможное действие |
|---|---|---|
| Лишняя цепочка редиректов | Правила Nginx, Apache, CDN и CMS | Свести переход к конечному URL и обновить внутренние ссылки |
| Важная страница возвращает 404 | URL, маршрутизацию, удаление или переименование страницы | Восстановить страницу либо настроить релевантный редирект |
| Soft 404 | Шаблон страницы ошибки и HTTP-статус | Возвращать настоящий 404 или 410 для отсутствующего ресурса |
| Неожиданный noindex | Настройки CMS, SEO-плагин, шаблон, X*-Robots-Tag | Удалять директиву только если страница должна индексироваться |
| Canonical ведёт на другой URL | Настройки канонизации и генерацию ссылок | Исправить источник canonical и согласовать его с sitemap |
| Ошибки 5xx | Логи приложения, PHP, Nginx или Apache, ресурсы сервера | Устранить причину сбоя, затем повторить запрос |
| Долгое время первого байта | Бэкенд, кэш, базу данных, CDN и сетевой маршрут | Профилировать приложение и устранять узкое место |
| Нет сжатия | Content-Encoding и настройки сервера | Включить gzip или Brotli для подходящих типов файлов |
7.3. Пример проверки конфигурации Nginx
Перед изменением конфигурации сделайте резервную копию. После редактирования сначала проверьте синтаксис:
sudo nginx -t
Если проверка завершилась успешно, конфигурацию можно перечитать без полного перезапуска сервера:
sudo systemctl reload nginx
Не копируйте универсальные правила редиректов в рабочий конфиг без проверки существующих server-блоков. На сайте за CDN или обратным прокси схема определения HTTPS может отличаться. Дублирующиеся правила способны вызвать циклические перенаправления или сделать сайт недоступным.
7.4. Пример проверки Apache
Для Apache проверка синтаксиса выполняется командой:
sudo apache2ctl configtest
Правила в .htaccess зависят от включённых модулей, разрешений AllowOverride и конфигурации виртуального хоста. Если сайт работает за прокси, учитывайте передачу исходной схемы запроса. После изменения повторно проверьте все варианты HTTP/HTTPS и www/без www.
7.5. Проверка robots.txt и noindex
Если важная страница закрыта от индексации, сначала определите источник директивы. Она может находиться в robots.txt, HTML-шаблоне, настройках CMS, SEO-плагине или HTTP-заголовке. Не удаляйте запрет автоматически: для служебных, личных и технических страниц он может быть намеренным.
Помните о различии между сканированием и индексацией. Если URL закрыт в robots.txt, поисковый робот может не получить страницу и не увидеть размещённый внутри неё meta* noindex. Поэтому директивы должны быть согласованы между собой.
7.6. Цикл повторной проверки
Рабочий процесс должен быть одинаковым до и после исправления:
- Сохраните исходный отчёт и зафиксируйте обнаруженную проблему.
- Определите её источник, не ограничиваясь внешним симптомом.
- Внесите одно контролируемое изменение.
- Повторите ту же команду или аудит для тех же URL.
- Сравните результаты и проверьте, не появились ли побочные ошибки.
- При необходимости используйте инструменты веб-мастера для проверки обработки страницы поисковой системой.
Изменение HTTP-ответа можно увидеть сразу, но повторное сканирование и обновление поискового индекса требуют времени. Успешный тест curl не означает, что поисковая система уже обновила данные о странице.
Итоговый план технического SEO-аудита
Для регулярной проверки сайта можно использовать следующий порядок:
- Собрать список важных URL из sitemap и структуры сайта.
- Проверить DNS, HTTPS и доступность основных адресов.
- Проверить robots.txt, sitemap.xml, canonical, Meta* Robots и X*-Robots-Tag.
- Найти неожиданные статусы, лишние редиректы и soft 404.
- Проанализировать серверные журналы и ошибки поисковых роботов.
- Измерить сетевое время ответа, проверить кэширование и сжатие.
- Сформировать отчёт, отделив подтверждённые технические ошибки от предупреждений.
- Исправить приоритетные проблемы и повторить проверки.
Заключение
Linux предоставляет достаточно инструментов для первичной диагностики технического состояния сайта. С помощью curl можно проверить HTTP-ответы, редиректы и заголовки, DNS-утилит — разрешение домена, системных команд — журналы сервера, а Python — автоматизировать проверку списка URL и собрать результаты в удобный CSV-отчёт.
Главное преимущество такого подхода — прозрачность. Каждая проверка выполняется конкретной командой, результат можно сохранить, повторить и сравнить после внесения изменений. Это особенно полезно при поиске причин ошибок после обновления CMS, переноса сайта, изменения серверной конфигурации или настройки редиректов.
При этом командный аудит следует считать частью технического SEO, а не полной заменой краулера, браузерного тестирования и инструментов поисковых систем. Надёжный вывод появляется тогда, когда данные HTTP, HTML, серверных логов и поисковой аналитики рассматриваются вместе.
Часто задаваемые вопросы
Можно выполнить ряд важных технических проверок: HTTP-статусы, заголовки, редиректы, время ответа и получение HTML. Но curl не исполняет JavaScript, не обходит автоматически все внутренние ссылки и не измеряет пользовательские Core Web Vitals.
Нет. Пример использует стандартные модули Python 3, поэтому достаточно установленного интерпретатора. Это упрощает запуск на обычном Linux-сервере.
Начните с доступности важных страниц, неожиданных 4xx и 5xx, цепочек редиректов, случайных noindex и корректности canonical. Затем переходите к метаданным, логам и производительности.
Код 200 подтверждает успешный HTTP-ответ, но не гарантирует индексацию. Причиной могут быть noindex, canonical на другой URL, дублирование, низкая ценность страницы, проблемы рендеринга или решение самой поисковой системы.
Частота зависит от размера и активности сайта. Небольшой ресурс можно проверять после крупных обновлений и регулярно по расписанию. Для часто обновляемого проекта полезен автоматический мониторинг ключевых URL и серверных ошибок.
Да, если проверять только разрешённые URL, ограничить частоту запросов, установить тайм-ауты и не создавать чрезмерную нагрузку. Для крупных сайтов начинайте с небольшого списка и постепенно увеличивайте объём проверки.
