Заголовки Last-Modified и If-Modified-Since (304 Not Modified): экономия краулинга
1. Архитектура и принцип работы
Первый запрос к странице возвращает 200 OK с заголовком Last-Modified: Tue, 12 Mar 2024 09:41:00 GMT. Поисковый робот или браузер кэширует дату вместе с URL. При следующем визите он шлёт If-Modified-Since с этой же датой. Сервер (nginx, Apache, CDN) сравнивает её с реальным mtime файла или записью в БД. Если ресурс не менялся — ответ 304 Not Modified с пустым телом: заголовки отдаются, контент нет. Робот понимает, что переобход не нужен, и тратит лимит краулинга на другие URL. Для динамики дату берут из поля updated_at в БД, для статики — из файловой системы. Работает в паре с ETag: If-None-Match имеет приоритет над If-Modified-Since, но 304 отдаётся при совпадении любого из валидаторов.
2. Математическая механика и триггеры ранжирования
Дата в Last-Modified обязана быть в формате RFC 1123 (IMF-fixdate): 'Tue, 12 Mar 2024 09:41:00 GMT', строго GMT, без миллисекунд. Сервер сравнивает If-Modified-Since с mtime с точностью до секунды: условие 304 срабатывает, если last_modified <= if_modified_since. В nginx это директива if_modified_since exact|before|off; по умолчанию exact, а before отдаёт 304 даже при равных датах — полезно для файлов, меняющихся редко. Apache использует mod_headers и FileETag MTime Size. Для динамики формула: Last-Modified = max(updated_at) по всем включённым сущностям страницы (контент, меню, виджеты). Кэш-слой (Varnish, Cloudflare) добавляет Age и хранит 304 до истечения TTL. Важно: заголовок не должен быть позже заголовка Date — иначе робот считает дату невалидной. Слабый валидатор: при изменении файла в ту же секунду, что и предыдущая версия, 304 может скрыть правку — поэтому критичные страницы дублируют ETag.
3. Специфика в Яндекс (YATI) vs Google
Оба робота шлют If-Modified-Since и уважают 304, но ведут себя по-разному. Яндекс активно использует Last-Modified при планировании переобхода: свежая дата повышает частоту краулинга, а стабильные 304 снижают её и экономят квоту хоста. Google больше опирается на ETag и If-None-Match, а Last-Modified учитывает как сигнал свежести; при конфликте валидаторов приоритет у ETag. Googlebot жёстче реагирует на некорректные даты (будущее время, не-GMT) — может игнорировать заголовок целиком. Яндекс терпимее к формату, но требует консистентности: если дата «прыгает» назад, робот перестаёт доверять сигналу. Для обоих 304 не считается ошибкой и не влияет на индексацию, но массовые 200 на неизменных страницах раздувают краулинговый бюджет.
4. Нормативы SLA и диагностическая матрица
| Параметр / Метрика | Эталон по SLA | Критическая ошибка |
|---|---|---|
| Доля ответов 304 на повторные запросы робота | >= 60% для стабильного контента | < 20% при неизменных страницах |
| Наличие Last-Modified в ответе 200 | 100% индексируемых URL | Отсутствует или не в GMT |
| Средний размер ответа при повторном обходе | < 1 КБ (только заголовки) | Полное тело HTML на каждом визите |
5. Пошаговый инженерный протокол внедрения
- Настройте отдачу Last-Modified для статики на уровне веб-сервера: в nginx — if_modified_since exact, в Apache — FileETag MTime Size; проверьте через curl -I, что заголовок присутствует и в GMT.
- Для динамических страниц формируйте дату из max(updated_at) по контенту, меню и виджетам, а не из текущего времени запроса — иначе каждый ответ будет 200 и краулинг сгорит впустую.
- Добавьте ETag рядом с Last-Modified и протестируйте условный запрос: curl -H 'If-Modified-Since: <дата>' -I https://site/page — ожидайте 304 Not Modified с пустым телом.
- Настройте кэш-слой (CDN/Varnish) на проксирование 304 и мониторьте долю 304 в логах: рост доли при стабильном контенте — признак корректной экономии краулинга.
# nginx: корректная отдача условных запросов
location / {
if_modified_since exact; # 304 при last_modified <= if_modified_since
etag on;
add_header Last-Modified "$date_gmt" always; # для динамики подставляйте дату из БД
}
# Проверка условного запроса
curl -sI -H 'If-Modified-Since: Tue, 12 Mar 2024 09:41:00 GMT' https://example.com/page
# Ожидаем: HTTP/1.1 304 Not Modified, пустое тело
# Формула даты для динамики:
# Last-Modified = max(updated_at) по контенту, меню и виджетам страницы
6. Типичные ошибки при оптимизации
- Отдавать Last-Modified, равный текущему времени запроса (now()) — робот видит «свежую» страницу на каждом визите, шлёт полный 200 и не экономит краулинг.
- Некорректный формат даты: локальная таймзона, ISO 8601 с миллисекундами или дата из будущего — валидатор отбрасывается, условные запросы не работают.
- Игнорировать ETag и полагаться только на секундную точность Last-Modified: правки в пределах одной секунды дают ложный 304, и робот не увидит обновление контента.
