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

Это 14-я часть цикла «WordPress на кэшируемом сайте».

1. Двойное подтверждение

Если подписывать адрес сразу, любой может подписать на ваш блог чужую почту — и вы будете слать письма человеку, который их не просил. Письма уйдут в спам, а репутация домена отправителя упадёт.

Поэтому после формы адрес попадает в базу со статусом «ждёт подтверждения», а на почту уходит письмо со ссылкой:

Кто-то (надеемся, вы) подписал этот адрес на уведомления о новых статьях блога.

Подтвердить подписку:
https://сайт/blog/?subscribe=confirm&t=<32 случайных символа>

Если это были не вы — просто проигнорируйте письмо, без подтверждения писем не будет.

Форма подписки защищена так же, как остальные: ловушка, время заполнения, не больше 5 попыток в час с адреса — иначе её используют, чтобы засыпать чужой ящик письмами подтверждения.

2. Ссылки из письма мимо кэша

Ссылка «подтвердить» ведёт на обычную страницу блога с параметрами. Страница блога кэшируется. Если ответ на эту ссылку попадёт в кэш, ничего страшного не случится сразу, но правильно, чтобы такие ответы в кэш не попадали вовсе: это действие, а не страница.

Обычный способ — nocache_headers() — здесь не сработает. В нашей конфигурации nginx игнорирует заголовок Cache-Control от PHP:

fastcgi_ignore_headers Cache-Control Expires;

Так сделано намеренно: WordPress и плагины любят слать «не кэшировать» на обычных страницах, и без этой строки кэш бы почти не наполнялся. Зато nginx слушает свой заголовок X-Accel-Expires:

add_action('template_redirect', function () {
    $action = $_GET['subscribe'] ?? '';
    $token  = preg_replace('~[^A-Za-z0-9]~', '', (string) ($_GET['t'] ?? ''));
    if (!in_array($action, ['confirm', 'stop'], true) || strlen($token) !== 32) {
        return;
    }
    // … найти подписчика по токену, сменить статус, запомнить сообщение для плашки
    nocache_headers();
    header('X-Accel-Expires: 0'); // nginx: этот ответ не сохранять
});

Проверка: два запроса подряд на ссылку подтверждения — оба MISS, то есть ответ ни разу не сохранился.

3. Рассылка только при первой публикации

  • рассылку запускает переход статьи в «опубликовано» из любого другого статуса;
  • в мету статьи сразу ставится отметка «в очереди», после завершения — время рассылки. Правка опубликованной статьи и повторная публикация после снятия рассылку не повторяют;
  • рассылка идёт кроном через минуту после выхода, пачками по 40 адресов, следующая пачка — ещё через минуту. Публикация не ждёт рассылку, а почтовый сервер не получает сотни писем разом.
add_action('transition_post_status', function ($new, $old, $post) {
    if ($post->post_type === 'post' && $new === 'publish' && $old !== 'publish'
        && !get_post_meta($post->ID, '_subscribers_notified', true)) {
        update_post_meta($post->ID, '_subscribers_notified', 'queued');
        wp_schedule_single_event(time() + MINUTE_IN_SECONDS, 'subscribe_send', [$post->ID, 0]);
    }
}, 10, 3);

Важно, что крон вообще работает: на сайте со страницами из кэша встроенный запуск почти не срабатывает (вторая часть).

4. Отписка в одно нажатие

В каждом письме — ссылка «отписаться» с тем же токеном и заголовок List-Unsubscribe: почтовые сервисы показывают по нему свою кнопку отписки рядом с отправителем. Человек, который нашёл, как отписаться, не нажимает «это спам».

Что пока не сделано — честно

  • подтверждение по простой ссылке. Некоторые почтовые фильтры сами открывают ссылки из писем, чтобы проверить их на вредоносность, — и такой фильтр подтвердит подписку без человека. Надёжнее страница подтверждения с кнопкой, которая отправляет POST;
  • нет заголовка List-Unsubscribe-Post для отписки в один клик по стандарту, который крупные почтовые сервисы требуют от массовых рассылок. Пока подписчиков немного, это не критично; при росте — добавить.

Нужна подписка на обновления сайта, которая не попадает в спам?

Пришлите адрес — сделаю подтверждение, отписку и рассылку пачками.

· полезно?
АЧ
Алексей Чикун веб-разработчик, команда TRIOHEAD

Делаю сайты и сервисы целиком: от вёрстки до сервера. Пишу о том, что проверил на своих проектах.

Комментарии

// загружаю комментарии…

Комментарии публикуются после проверки. Ссылки и HTML не принимаются.