Содержание
Ситуация следующая. У вас есть сервер за 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();