← Назад к заметкам

Заметки

Тему письма в mail() нужно кодировать отдельно

Почему русская тема письма через PHP mail() может отображаться некорректно и как закодировать её в UTF-8.

Для тела письма можно указать charset=UTF-8, но для темы этого недостаточно. Тема находится в заголовке Subject, поэтому её нужно кодировать отдельно.

Проблема

При отправке письма через mail() русская тема может прийти в почтовый клиент кракозябрами, даже если тело письма отображается нормально.

Тело письма и тема письма кодируются по-разному. Для тела обычно достаточно указать кодировку в заголовке:

Content-Type: text/plain; charset=UTF-8

Но тема письма — это заголовок Subject. Для русского текста в заголовке нужен формат MIME encoded-word:

=?UTF-8?B?BASE64_TEXT?=

Если передать русскую тему в mail() как обычную строку, часть почтовых клиентов может отобразить её неправильно.

Решение

Перед отправкой тему нужно преобразовать в MIME encoded-word. Для простого случая подойдёт вариант с UTF-8 и Base64.

Минимальный вариант:

<?php

$subject = 'Заявка обновлена';
$encoded_subject = '=?UTF-8?B?' . base64_encode($subject) . '?=';

Лучше вынести кодирование в отдельную функцию, чтобы не собирать заголовок вручную в каждом месте отправки.

<?php

/**
 * Кодирует тему письма для заголовка Subject.
 */
function encodeMailSubject(string $subject): string
{
    return '=?UTF-8?B?' . base64_encode($subject) . '?=';
}

Пример отправки

Пример ниже показывает базовую отправку текстового письма через mail(). Для очередей, повторных попыток и массовых уведомлений лучше использовать отдельный почтовый механизм.

<?php

const MAIL_TO = 'user@example.com';
const MAIL_FROM = 'noreply@example.com';

$subject = 'Заявка обновлена';
$message = 'В CRM изменилась заявка.';

sendPlainTextMail(MAIL_TO, MAIL_FROM, $subject, $message);

/**
 * Отправляет простое текстовое письмо.
 */
function sendPlainTextMail(string $recipient_email, string $sender_email, string $subject, string $message): bool
{
    $encoded_subject = encodeMailSubject($subject);
    $headers = buildMailHeaders($sender_email);

    return mail($recipient_email, $encoded_subject, $message, $headers);
}

/**
 * Кодирует тему письма для заголовка Subject.
 */
function encodeMailSubject(string $subject): string
{
    return '=?UTF-8?B?' . base64_encode($subject) . '?=';
}

/**
 * Собирает заголовки письма.
 */
function buildMailHeaders(string $sender_email): string
{
    $headers = [
        'MIME-Version: 1.0',
        'Content-Type: text/plain; charset=UTF-8',
        'From: ' . $sender_email,
    ];

    return implode("\r\n", $headers);
}

В этом примере тема кодируется отдельно, а тело остаётся обычным UTF-8 текстом. Кодировка тела задаётся через Content-Type.