Ситуация следующая. У вас есть сервер за Cloudflare и вы не хотите светить его ip в заголовках письма. Или у вас хостер закрыл 25й порт, ну или сервер стоит дома и провайдер закрыл 25й порт. Вариантов множество.
Есть решение. Но как всегда с подводными камнями в настройке и работе.
Для начала устанавливаем на Centos
yum install msmtp
Или Ubuntu
apt-get install msmtp
Говорят, нужно создать директорию
~/ named .msmtprc
Но я просто создал файл конфига на Ubuntu
nano /etc/msmtprc
defaults tls on tls_starttls on tls_certcheck off logfile /var/log/msmtp account mail_yandex host smtp.yandex.ru port 25 auth on user info@****.com.ua password ************ from info@****.com.ua account mail_server_2 host smtp.hd.zp.ua port 25 auth on user [email protected] password ******* from [email protected] account default : mail_server_2
Тут вы можете указать сразу несколько конфигов для подключения к почтовым серверам и использовать их как вам будет угодно в дальнейшем, о чём чуть дальше.
Обратите внимание, что по умолчанию мы указали 2й конфиг с названием mail_server_2
И так, сразу возможно возникнет проблема с тем, что msmtp не сможет писать лог.
Можно встретить примеры конфигов, где файл лога указывается как logfile /var/log/msmtp.txt
Но выдача прав на файл и вообще любые танцы с бубном ни к чему не привели. Поэтому используем без .txt
Если вдруг ничего не помогло:
sudo touch /var/log/msmtp sudo chown -R root:mail /var/log/msmtp sudo chmod -R 660 /var/log/msmtp sudo chown root:msmtp /etc/msmtprc sudo chmod 640 /etc/msmtprc
Дальше нужно установить симлинки
ln -s /usr/bin/msmtp /usr/sbin/sendmail ln -s /usr/bin/msmtp /usr/bin/sendmail ln -s /usr/bin/msmtp /usr/lib/sendmail
у меня ругалось что sendmail есть и пришлось его ликвидировать
sudo rm /usr/sbin/sendmail
После этого устанавливаем.
После этого необходимо подменить отправку в php.ini:
sendmail_path = "/usr/bin/msmtp -t"
Для виртуальныж хостов вызываем нужную нам секцию отправки:
php_admin_value sendmail_path "/usr/bin/msmtp -a mail_yandex -t"
Тут вы указываете какой конфиг использовать для отправки почты. Ведь у вас может быть много сайтов на сервере и много разных ящиков для отправки почты.
Для VestaCP или Hestia нужно указать почту в секции nano /home/пользователь/conf/web/хост/apache2.ssl.conf
<Directory /home/caca/web/hd.zp.ua/public_html> AllowOverride All SSLRequireSSL Options +Includes -Indexes +ExecCGI php_admin_value sendmail_path "/usr/bin/msmtp -a mail_yandex -t" </Directory>
И перегружаем Apache:
sudo service apache2 restart
Вроде бы и всё, но нет.
Можно протестировать почту из консоли
echo "Test message." | msmtp -a mail_yandex [email protected]
Почта должна уйти.
Если уже прописали в виртуальные хосты, то также должна уходить, но есть два момента:
1. Почта не уходит. Возможно, php.ini для консоли и для виртуального хоста используется разный. Проверьте.
2. Почта с сайта уходит очень долго и поэтому всё жутко тупит до отправки. Ведь php скрипт ждёт true в ответ на отправку почты и всё это время сайт для вас висит.
Решение костыльное, но есть. Но увы, нужны минимальные знания php и вашего скрипта.
Нужно либо добавить в скрипт асинхронную отправку почты, либо вместо отправки из скрипта, сделать скрипт добавления писем в базу данных Mysql и раз в 1-5 минут по cron делать отправку.
Создаём таблицу MySQL
CREATE TABLE messages ( id INT AUTO_INCREMENT PRIMARY KEY, to_email VARCHAR(255) NOT NULL, from_email VARCHAR(255) NOT NULL, subject VARCHAR(255) NOT NULL, message TEXT NOT NULL, mail_headers TEXT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP );
Для примера, форум SMF
Файл Subs-Post.php
Вместо
if (!mail(strtr($to, array("\r" => '', "\n" => '')), $subject, $message, $headers, sprintf("-oi -f %s", $webmaster_email)))
{
log_error(sprintf($txt['mail_send_unable'], $to));
$mail_result = false;
}
$to = strtr($to, array("\r" => '', "\n" => ''));
$smcFunc['db_insert']('',
'messages', // Название таблицы для логирования
array(
'subject' => 'string',
'message' => 'string',
'mail_headers' => 'string',
'from_email' => 'string',
'to_email' => 'string',
),
array(
'subject' => $subject,
'message' => $message,
'mail_headers' => $headers,
'from_email' => $webmaster_email,
'to_email' => $to,
),
array('id')
);
для DLE
в файле mail.class.php
Вместо
if( !@mail( $this->to, $this->subject, $this->message, $this->mail_headers, $this->additional_parameters ) ) {
if( !@mail( $this->to, $this->subject, $this->message, $this->mail_headers) ) {
$this->smtp_msg = "PHP Mail Error.";
$this->send_error = true;
}
}
$to = $db->safesql($to);
$from = $db->safesql($this->from);
$subject = $db->safesql($subject);
$message = $db->safesql($this->message);
$mail_headers = $db->safesql($this->mail_headers);
$query = "INSERT INTO messages (to_email, from_email, subject, message, mail_headers) VALUES ('$to', '$from', '$subject', '$message', '$mail_headers')";
$db->query($query);
В итоге у нас будут заносится наши письма с нужными заголовками в базу данных. Теперь нужно создать скрипт отправки по cron
send_mes.php
<?php
// Подключение к базе данных
$mysqli = new mysqli('localhost', 'пользователь', 'пароль', 'ваша_база_данных');
// Проверка соединения
if ($mysqli->connect_errno) {
echo "Не удалось подключиться к MySQL: " . $mysqli->connect_error;
exit();
}
// Получение сообщений из очереди
$query = "SELECT * FROM messages";
$result = $mysqli->query($query);
$idsToDelete = array(); // Массив для хранения идентификаторов записей, которые нужно удалить
if ($result->num_rows > 0) {
// Отправка сообщений
while ($row = $result->fetch_assoc()) {
$to = $row['to_email'];
$subject = $row['subject'];
$message = $row['message'];
$mail_headers = $row['mail_headers'];
$id = $row['id'];
// Отправка письма
if (mail($to, $subject, $message, $mail_headers)) {
// Добавляем идентификатор записи в массив для удаления
$idsToDelete[] = $id;
echo "Отправлено $to.";
} else {
echo "Ошибка при отправке сообщения на адрес $to.";
}
}
} else {
echo "Нет сообщений в очереди.";
}
// Удаление записей после завершения цикла
if (!empty($idsToDelete)) {
$idsString = implode(',', $idsToDelete);
$deleteQuery = "DELETE FROM messages WHERE id IN ($idsString)";
if ($mysqli->query($deleteQuery)) {
echo "Удалено записей из базы данных: " . count($idsToDelete);
} else {
echo "Ошибка при удалении записей из базы данных: " . $mysqli->error;
}
}
// Закрываем соединение
$mysqli->close();