Гигиена базы и sunset-политика: как мёртвые адреса топят доставляемость живых
Мёртвые и незаинтересованные адреса — это не спящий актив, а налог на inbox placement живых подписчиков. Разбираем инженерную sunset-машину: recency-сегментация по кликам, детерминированная классификация bounce по SMTP-кодам, жёсткий ре-энгейджмент с дедлайном и permanent suppression через хеш.
EvilMail Team14 июля 2026 г.11 мин чтения
Мёртвые адреса — это налог на доставляемость живых
Почтовый провайдер не оценивает каждое ваше письмо по отдельности. Он смотрит на весь поток с вашего домена и IP разом: какая доля улетает в bounce, какая помечается как спам, сколько получателей вообще открывают и кликают. Эта агрегированная репутация решает, куда попадёт следующее письмо — в «Входящие», в «Промоакции» или в спам. И решает она это для всей рассылки сразу, а не персонально для каждого адресата.
Отсюда неприятный вывод: каждый невалидный или давно молчащий ящик в вашем списке тратит репутационный бюджет живых подписчиков. Вы отправили 100 000 писем, из них 15 000 ушло на адреса, где никто не читает почту год, и ещё 3 000 просто отскочило от несуществующих ящиков. Gmail видит поток с низким engagement и заметным bounce — и понижает inbox placement для всех, включая тех 40 000 человек, которые вас реально ждут. Мёртвая часть базы буквально забирает доставляемость у живой.
С 1 февраля 2024 года это перестало быть темой для дискуссий. Gmail и Yahoo ввели единые требования для отправителей от 5000 писем в день на один домен, и они обязательны:
Spam complaint rate < 0.3% по данным Google Postmaster Tools. Реальная рабочая цель — держаться ниже 0.1%; на 0.3% уже начинается throttling.
One-click unsubscribe
Sunset-политика и гигиена базы подписчиков: сегментация, ре-энгейджмент, suppression | EvilMail — EvilMail Blog
обязателен: заголовки
List-Unsubscribe: <https://...>, <mailto:...>
вместе с
List-Unsubscribe-Post: List-Unsubscribe=One-Click
(RFC 8058), и отписка должна отрабатывать в течение двух дней.
SPF, DKIM и DMARC настроены и выровнены по домену (минимум запись DMARC с p=none и рабочим alignment).
Sunset-политика — это не гигиена ради аккуратности. Это защита канала от собственного балласта. Дальше — как построить её инженерно, а не на глаз.
Активность в 2026 — это клик, а не открытие
Если ваша сегментация неактивных строится на open rate, она сломана. С сентября 2021 года Apple Mail Privacy Protection префетчит все трекинг-пиксели через свой прокси — вне зависимости от того, открыл человек письмо или нет. Open rate по аудитории Apple Mail завышается на 50–70%, а геолокация по IP из пикселя становится мусором (запрос идёт с прокси Apple). Добавьте сюда Gmail Image Proxy, который кэширует картинки ещё до открытия письма, и корпоративные security-сканеры, которые на входе дёргают каждую ссылку, — и «открытие» превращается в сигнал, который с равной вероятностью означает живого человека или робота-антивирус.
Что считать реальным сигналом активности:
клик по ссылке в письме (last_click_at);
транзакционное действие — логин, покупка, вход в личный кабинет;
ответ на письмо.
Клики тоже не стерильны: те же security-сканеры генерируют фантомные клики, поэтому транзакционное действие остаётся самым чистым сигналом. Но клик несравнимо ближе к живому человеку, чем открытие. Открытия хранить можно — но только как справочную колонку last_open_at, а не как основание для решений. Если вы построите sunset на открытиях, вы вычистите как раз активных читателей (которые кликают, но не «открывают» из-за клиента без загрузки картинок) и оставите ботов Apple, которые прилежно «открывают» каждое письмо. Решение по адресу принимает только last_click_at.
Модель жизненного цикла: recency-бакеты
Никакого ML для начала не нужно. Достаточно RFM-lite — разбить базу по дате последнего клика на четыре бакета. Для еженедельной рассылки рабочие пороги:
Active — клик за последние 0–90 дней;
Cooling — 90–180 дней;
Dormant — 180–365 дней;
Sunset — 365+ дней без клика.
Пороги подстраиваются под частоту отправки. Ежедневная рассылка — окна уже (человек, не кликавший 60 дней при семи письмах в неделю, уже холодный). Квартальный ньюслеттер — окна шире, там и год молчания нормален. Ключевое правило: переход в Sunset запускает ре-энгейджмент, а не мгновенный DELETE. Sunset — это состояние, а не команда удаления.
Классификация bounce по SMTP-кодам
Bounce нельзя разбирать «на глаз» по тексту письма. SMTP-код детерминирован — по нему строятся жёсткие правила. Вот строка из лога Postfix для типичного hard bounce:
Ключевое поле — dsn=5.1.1 и код 550 5.1.1. Правила разбора:
Hard bounce — 550 5.1.1 (нет такого пользователя), 550 5.1.2 (домен не принимает почту). Suppress немедленно, ноль ретраев. Адрес мёртв, повторять бессмысленно и вредно для репутации.
Soft / временный — любые 4.x.x: 421 (rate-limited), 450, 452 (too many recipients). Экспоненциальный бэкофф, ретраи по расписанию. Это не про адрес, это про момент.
Mailbox full — 452 5.2.2 / 552. Серая зона: ретраить ограниченно, но suppress после 5 отказов подряд за 72 часа. Заброшенный переполненный ящик — почти всегда мёртвый.
Reputation / auth block — 550 5.7.1, 550 5.7.26. Это не про адрес, это про вас. Провайдер отверг письмо из-за вашей аутентификации или репутации. Suppress здесь — грубейшая ошибка новичка: вы выбросите валидные адреса и спрячете реальную проблему. Чинить DKIM/DMARC/SPF, а не вычищать список.
Sunset-политика в SQL
Хранить plaintext удалённых адресов не нужно и с точки зрения GDPR вредно. Правильный подход — permanent suppression по SHA-256-хешу: при каждой отправке адрес хешируется и проверяется по suppression_list через LEFT JOIN. Так вы никогда не перешлёте письмо на подавленный адрес, но и не храните персональные данные тех, кого удалили по «праву на забвение».
-- кандидаты в Sunset: активны когда-то, но без клика 365+ дней, ещё не подавлены
SELECT s.id, s.email
FROM subscribers s
LEFT JOIN suppression_list sup
ON sup.email_hash = encode(digest(lower(s.email),'sha256'),'hex')
WHERE s.last_click_at < now() - interval '365 days'
AND s.status = 'active'
AND sup.email_hash IS NULL;
-- permanent suppression: храним хеш, а не plaintext удалённого адреса
INSERT INTO suppression_list (email_hash, reason, created_at)
VALUES (encode(digest(lower($1),'sha256'),'hex'), 'hard_bounce', now())
ON CONFLICT (email_hash) DO NOTHING;
ON CONFLICT DO NOTHING даёт идемпотентность — один и тот же адрес можно «подавлять» сколько угодно раз без ошибок. А проверка через LEFT JOIN ... WHERE sup.email_hash IS NULL на каждой выборке гарантирует, что подавленный адрес физически не попадёт в очередь отправки, даже если он всё ещё лежит в subscribers.
Ре-энгейджмент: три письма, жёсткий дедлайн
Бесконечная серия «мы скучаем» — это не ре-энгейджмент, а медленное самоубийство репутации: вы полгода долбите незаинтересованных, накапливая жалобы. Рабочая схема жёсткая и короткая:
3 письма за 14 дней. Первое — прямой вопрос «вы ещё с нами?». Второе (день 7) — что человек теряет. Третье (день 13) — явный дедлайн: «если не подтвердите до завтра, мы отключим рассылку».
One-click resubscribe в каждом письме — крупная кнопка, ведущая на клик-эндпоинт.
Кликнул → возвращаем в active, обнуляем счётчик. Не кликнул → suppress с reason = 'sunset'.
Важно: подавление — это смена статуса и запись хеша, а не DELETE строки. Строка subscribers нужна для дедупликации будущих импортов — если тот же адрес зальют повторно из другого источника, вы не начнёте слать ему заново. И не отправляйте ре-энгейджмент на hard-bounced адреса — они уже мертвы, это только жжёт репутацию.
Про ожидания: реактивация 3–8% на dormant-сегменте — это норма, а не провал. Если вернулось 5% — вы вернули реальных людей и очистили остальные 95% от балласта. Это выигрыш, а не потеря базы.
Ловушки и почему SMTP-верификация опасна
Спам-ловушки бывают двух видов, и второй прямо объясняет порог в 365 дней:
Pristine traps — адреса, которых никогда не существовало, посеянные провайдерами и антиспам-организациями по вебу. Попадают в базу через парсинг и покупные списки. Один такой адрес — сигнал провайдеру, что вы собираете почту нечисто.
Recycled traps — реальный когда-то ящик, заброшенный владельцем. Провайдер держит его в статусе «нет пользователя» примерно 12 месяцев, а затем реактивирует как ловушку. Вот почему порог Sunset именно 365+ дней: адрес, молчащий год, с высокой вероятностью уже стал recycled-ловушкой.
Теперь про соблазн «просто проверить адреса» через SMTP-пробинг (RCPT TO без DATA). Это ненадёжно и опасно одновременно:
catch-all домены отвечают 250 OK на любой адрес — ложный positive, вы считаете мёртвый адрес живым;
greylisting возвращает 451 на совершенно валидный ящик — ложный negative, вы выбрасываете живого;
массовый пробинг с вашего sending-IP детектится провайдерами как directory harvest attack (перебор адресов) и роняет репутацию IP быстрее, чем любая грязная рассылка.
Для evilmail это фундаментально: temp/disposable-домены — это фабрика recycled-ловушек и одноразовых ящиков по определению. Гигиена начинается на этапе сбора, а не рассылки. Фильтруйте disposable-домены на входе — проверка MX плюс список известных временных провайдеров до записи в базу. Отсеять мусорный адрес на форме регистрации в сто раз дешевле, чем вычищать его последствия из репутации домена.
Чек-лист гигиены перед каждым большим сендом
Opt-in подтверждён — в списке только те, кто явно подписался (double opt-in по возможности).
List-Unsubscribe + List-Unsubscribe-Post присутствуют, one-click отписка отрабатывает за < 2 дней.
Suppression_list вычтен — выборка идёт через LEFT JOIN ... IS NULL, подавленные не в очереди.
Disposable-домены отфильтрованы на этапе сбора (MX + список известных temp-провайдеров).
Hard-bounce из прошлой рассылки уже перенесены в suppression с reason = 'hard_bounce'.
Spam rate в Postmaster Tools < 0.3% (цель < 0.1%) за последнюю неделю.
DMARC `p=quarantine` или `reject` с выровненным DKIM; SPF проходит.
Sunset-переходы отработаны — dormant-сегмент прогнан через ре-энгейджмент, молчащие подавлены, а не удалены.