Пиксели отслеживания в письмах: как их читают по логам и как заблокировать на сервере
Пиксель отслеживания — это не магия, а обычный HTTP GET за картинкой 1x1. Разбираем, что именно утекает при «открытии» письма, почему клиентские настройки дырявые, и строим защиту тремя эшелонами на Rspamd и Postfix.
EvilMail Team4 августа 2026 г.11 мин чтения
## Что реально происходит, когда вы «открыли письмо»
Никакого «открытия» в письме не заложено. В SMTP нет обратного канала, который сообщал бы отправителю, что вы прочитали сообщение. То, что маркетологи называют open, — побочный эффект рендеринга HTML: почтовый клиент встречает тег `
с внешним адресом и честно идёт за картинкой по HTTP. Сервер на той стороне логирует запрос. Всё.
Вот как выглядит эта «телеметрия» в сыром виде — строка из access-лога nginx трекингового сервера:
203.0.113.44 - - [08/Jul/2026:11:22:03 +0000] "GET /o/eJxNkMtq...==/pixel.gif HTTP/2.0" 200 43 "-" "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15"
Из одной строки ESP извлекает пять фактов: ваш IP-адрес (203.0.113.44), метку времени с точностью до секунды, идентификатор кампании и получателя, закодированный в пути (eJxNkMtq...== — обычно base64 или зашифрованный blob вида campaign_id + recipient_id), User-Agent (iPhone, iOS 17.5) и код ответа. Отдаётся 43 байта — прозрачный GIF 1×1.
Ключевой момент: загрузка ресурса и чтение человеком — не одно и то же. Пиксель фиксирует, что ваш клиент отрендерил HTML. Смотрели ли вы на письмо — он не знает.
## Анатомия пикселя: как выглядит трекер в исходнике
Откройте любое маркетинговое письмо через «Показать оригинал» (Gmail) или сохраните его как .eml и загляните в HTML. Классический пиксель выглядит так:
``html
Как работают пиксели отслеживания в email и как заблокировать их на сервере — EvilMail Blog
Разбор по частям: домен трекера (track., t., email., links., open. — почти всегда поддомен ESP или отдельный «нейтральный» хост), путь с идентификатором получателя и кампании, нулевые размеры и display:none, чтобы вы ничего не заметили. В заголовках ответа почти всегда стоит Cache-Control: no-store — чтобы каждое повторное открытие давало новый запрос, а не отдавалось из кеша.
Но пиксель — не единственный носитель. Отслеживание встраивают ещё как минимум четырьмя способами:
- **Редирект-ссылки.** Каждая кнопка ведёт не на целевой URL, а на https://click.esp.com/CL0/https://real-url — клик логируется, потом вас редиректят.
- **CSS background-image.** — тот же GET, но без тега
, обходит наивные фильтры «вырежем все картинки».
- **Внешние веб-шрифты** через @font-face с внешним src.
- **** и другие предзагрузочные подсказки.
Поэтому «блокировка картинок» на клиенте по определению неполна: она не трогает CSS-фоны, шрифты и ссылки-редиректы.
## Что именно раскрывается — и чего пиксель НЕ видит
Что видит и чего не видит пиксель
Видит
• IP-адрес → гео, ISP, город
• User-Agent → клиент + ОС
• Точное время открытия
• Число и время повторов
• Referrer (иногда)
• Факт рендеринга HTML
Не видит
• Прочитал ли человек
• Сколько времени читал
• Скролл, движение курсора
• Реальный IP при Apple MPP
• Реальный IP при Gmail-прокси
Apple MPP префетчит всё → массовые ложные «открытия»
Теперь про искажения, из-за которых open-rate как метрика после 2021 года наполовину мёртв:
- **Apple Mail Privacy Protection** (с iOS 15, осень 2021). Apple Mail префетчит все изображения через свой прокси-CDN ещё до того, как вы открыли письмо. Отправитель видит IP-диапазон Apple и обобщённый macOS User-Agent, а не ваши. Итог: массовые ложные «открытия» у всех пользователей Apple Mail с включённой защитой — а это большинство.
- **Gmail image proxy.** Gmail проксирует и кеширует все внешние картинки через ci*.googleusercontent.com. Реальные IP и User-Agent скрыты, открытие фиксируется один раз — в момент проксирования.
Но не обольщайтесь: на Outlook (десктоп с загрузкой картинок), Thunderbird с включёнными внешними ресурсами, множестве мобильных клиентов сторонних ESP и веб-клиентах без строгой CSP пиксель по-прежнему сдаёт ваш настоящий IP и UA.
## Почему клиентские настройки не спасают
Стандартный совет «отключите загрузку внешних изображений» звучит разумно, пока не столкнёшься с реальностью:
- Настройку отключают через неделю, потому что половина легитимных писем без картинок нечитаема.
- Apple MPP грузит изображения независимо от вашего желания — это не защита от трекинга контента, а подмена метаданных.
- Корпоративные подписи с логотипами и баннеры уведомлений тянут внешние ресурсы, и пользователь привыкает жать «загрузить».
- Каждый клиент настраивается отдельно: телефон, десктоп, веб. Забыли на одном — утечка.
Вывод простой: единственная точка, через которую проходит весь входящий поток, — ваш MTA. Санитайзинг на Postfix и Rspamd закрывает сразу все клиенты, а не по одному.
Жизненный цикл пикселя и три эшелона защиты
течёт: IP · User-Agent · timestamp
1. ESP встраивает
<img src=track/ID>
2. Доставка
в почтовый ящик
3. Клиент
HTTP GET картинки
4. Трекер
логирует IP+UA+time
отдаёт 1x1 GIF
5. Дашборд
«открыто»
Щит C · strip контента
режет <img> между 1 и 2
Щит B · Rspamd blocklist
маркирует на узле 2
Щит A · image proxy
подменяет IP/UA на 3→4
Все три щита стоят на MTA — покрывают любой почтовый клиент сразу
## Эшелон 1: проксирование и переписывание картинок на сервере
Идея — не блокировать картинки, а подменять источник запроса. Все теги
переписываются на ваш кеширующий прокси на входе. Когда клиент грузит картинку, запрос уходит с вашего серверного IP и обобщённого User-Agent, а ESP видит один и тот же адрес для всех получателей. Реальный пользователь становится неотличим от фонового шума.
На практике это milter или content-filter, который в теле HTML заменяет:
src="https://track.esp.com/o/ID/pixel.gif"
→ src="https://imgproxy.local/?url="
Ваш imgproxy (готовый imgproxy или тонкий nginx с proxy_pass и кешем) ходит за оригиналом, кеширует и отдаёт клиенту. Компромиссы честные: это ломает точный кеш на стороне клиента, добавляет нагрузку на прокси и иногда портит вёрстку писем со сложным CSS. Зато трекер получает мусорные данные. По сути это то, что Gmail и Apple делают за вас, — только под вашим контролем.
## Эшелон 2: блок-лист трекинг-доменов в Rspamd
Rspamd разбирает URL из тела письма и сверяет их с картой. Заводим multimap по трекинг-доменам и вешаем символ со score:
tracking_pixel {
type = "url";
filter = "tld";
map = "/etc/rspamd/maps/tracking_domains.map";
symbol = "TRACKING_PIXEL";
score = 2.0;
}
В tracking_domains.map — известные хосты и паттерны ESP:
track.example.com
t.sendgrid.net
email.mailchimp.com
links.example.com
click.esp.com
open.example.net
Символ TRACKING_PIXEL со score 2.0 не отклоняет письмо, а поднимает его спам-скор и даёт зацепку для действий: пометить заголовком X-Tracking: yes, положить в отдельную папку через Sieve или в связке с эшелоном 3 запустить strip. Держите score умеренным — легитимные рассылки тоже содержат трекинг, и агрессивный вес отправит нужные письма в спам.
Для параноиков есть инструмент погрубее — DNS sinkhole. На резолвере (Unbound) заворачиваем трекинг-домены в петлю:
local-zone: "track.example.com" redirect
local-data: "track.example.com A 127.0.0.1"
Тогда
просто не резолвится наружу — картинка не грузится ни у одного клиента в сети. Минус: sinkhole бьёт по домену целиком (если ESP смешивает контент и трекинг на одном хосте — сломаете и картинки) и не трогает редирект-ссылки, которые пользователь кликает руками.
## Эшелон 3: полный strip внешнего HTML-контента
Самый жёсткий уровень для критичных ящиков — вырезать внешние ресурсы физически на входе. Ставим content-filter в Postfix. В master.cf:
smtp inet n - n - - smtpd
-o content_filter=stripimg:dummy
stripimg unix - n n - 10 pipe
flags=Rq user=filter argv=/usr/local/bin/strip-remote-img.py -f ${sender} -- ${recipient}
Скрипт strip-remote-img.py парсит MIME, в каждой text/html-части удаляет
с внешним src, вычищает url() из inline-стилей и внешние , оставляя только вложения по cid:. Затем реинжектит письмо обратно через sendmail -i. Логика по шагам:
- разобрать сообщение (email.message_from_bytes);
- для каждой HTML-части прогнать через парсер (bleach или свой на lxml), удалить img[src^=http], img[src^=//], атрибуты style с url(, теги link и style с внешними ссылками;
- сохранить cid:-картинки — это встроенные вложения, они не утекают наружу;
- собрать сообщение и отдать в sendmail -i -f sender recipient.
То же самое чище делается на Lua прямо в Rspamd, без отдельного демона в pipe. Когда это оправдано: ящики для журналистов, юристов, финансистов, где раскрытие IP и факта прочтения — реальный риск. Для обычной переписки это перебор: сломаете вёрстку легитимных писем и получите поток жалоб.
## Как временная почта evilmail.pro закрывает вопрос
Одноразовый адрес решает проблему на уровне архитектуры, а не настроек. Пиксель шифрует связку «идентификатор ↔ личность», но если адрес не привязан к вам, шифровать нечего. Входящие письма на evilmail.pro рендерятся в изолированном веб-вьюере: внешние ресурсы не грузятся автоматически, а когда грузятся — идут через прокси, так что IP пользователя наружу не уходит. Трекер в лучшем случае увидит один запрос с нашего IP и обобщённым UA. Пиксель либо не срабатывает, либо сдаёт бесполезные данные. Это не прикрученная сверху фича — это следствие модели: нет постоянного ящика, нет постоянной цели для отслеживания.
## Чек-лист: аудит и блокировка за 15 минут
- **Найдите пиксели.** grep -Eo '
]*width="1"[^>]*>' message.eml и поиск по display:none и 1x1.
- **Проверьте свой клиент** — грузит ли он внешние картинки по умолчанию. Отключите автозагрузку осознанно.
- **Разверните Rspamd multimap** с трекинг-доменами и символом TRACKING_PIXEL (score ~2.0).
- **Настройте действие** на символ: заголовок, Sieve-сортировка или запуск strip.
- **Опционально — DNS sinkhole** на самые злостные трекинг-хосты через Unbound.
- **Для критичных ящиков** поставьте content_filter со стрипом внешних
и url()`.
- Не забудьте редирект-ссылки — пиксельные фильтры их не ловят, нужен отдельный разбор URL.
- Замерьте эффект со стороны отправителя: open-rate по этим ящикам должен упасть до нуля или стать шумом Apple/Gmail-прокси.
Пиксель отслеживания живёт ровно до того момента, пока запрос за картинкой уходит с вашим IP. Перехватите этот GET на сервере — и контроль над «открытием» возвращается к вам, а не остаётся у того, кто прислал письмо.