Что делать, если не отправляется почта из PHP
Тривиальные ситуации, когда имеется непорядок со значением sendmail_path
в конфигурации php, решаются легко. Проверьте через phpinfo();
, чтобы у вас было задано значение типа /usr/sbin/sendmail -t -i
в конфиге, и чтобы за /usr/sbin/sendmail
действительно скрывался установленный и работающий MTA. Но сегодня пришлось побеждать менее типовую проблему: из php-скрипта почта как бы отправлялась, но где-то дальше сообщения терялись.
Убеждаемся, что PHP точно пытается отправить почту
Для начала напомню, что начиная с php 5.3 можно включить лог для всей исходящей почты, отсылаемой через функцию mail()
из php. Достаточно в конфигурацию php.ini
добавить такую настройку:
mail.log = /var/log/phpmail.log
При этом важно, чтобы пользователь, от которого работает php, имел доступ к файлу /var/log/phpmail.log
на запись. Рекомендую для комфортной отладки создать файл и позволить всем в него записывать, примерно так:
touch /var/log/phpmail.log chmod 666 /var/log/phpmail.log
Если у вас php работает как модуль Apache, то перезапустите последний.
Записи в логе будут примерно такие:
mail() on [/var/www/modules/system/system.mail.inc:83]: To: admin@example.tld -- Headers: From: user@example.tld
Если версия php у вас поновее чем 5.3, то в лог добавятся дата и время (что, конечно, очень полезно). Например, PHP версии 5.5.32 точно добавляет дату и время (но с какой в точности версии это началось — я не знаю). Всё-таки главное в этом логе — абсолютный адрес скрипта и номер строки, с которой вызывалась функция.
В общем, следующий тривиальный скрипт должен обязательно оставить след в логе:
<?php $to = 'yourmail@example.tld'; $subject = 'Test email subject'; $message = 'Test from PHP at ' . date('Y-m-d H:i:s'); mail($to, $subject, $message); echo 'Mail sent'; ?>
У меня была как раз такая ситуация: php исправно делал свою работу и записи в логе почты оставлял (ничего при этом не оставляя в логе ошибок).
Проверяем, что письмо может отправить администратор из консоли (без всяких там PHP)
Одновременно с этим почта у меня прекрасно отправлялась из серверной консоли. Проверить можно так:
echo "Test message" | mail -s "Test emal subject" yourmail@example.tld
Если вдруг на вашем сервере нет утилиты mail
, то установите пакет mailx
вот так:
yum -y install mailx
Анализируем лог postfix
Пришлось заглядывать в лог почтовика. Он пишется в файл /var/log/maillog
(актуально для CentOS).
И вот там-то нашлись неприятные записи такого характера:
18:34:31 postfix/sendmail[26381]: fatal: chdir /var/spool/postfix: Permission denied 18:51:16 postfix/sendmail[4603]: fatal: chdir /var/spool/postfix: Permission denied
Даже минимальный опыт общения с линуксом подсказывает, что тут дело либо в несоответствии прав/владельцев, либо в какой-то дефолтной защите, типа фаерволла или… SELinux. В данном случае «виноват» последний.
Отучаем SELinux блокировать почту, отправляемую из PHP
Проверить можно такой командой:
getsebool httpd_can_sendmail
Если вывод будет httpd_can_sendmail --> off
, то значит вашу почту блокирует SELinux. Чтобы его от этого отучить на постоянной основе выполните команду:
setsebool -P httpd_can_sendmail 1
И после этого ждите несколько секунд, пока снова не увидите приглашение командной строки. Без ключа -P
блокировка снимется только до перезагрузки.
Кстати, пусть вас не смущает, что в параметрах SELinux упомянут httpd. Даже если у вас Apache отсутствует в принципе, а используется, например, связка nginx и php-fpm, то почта всё равно может блокироваться с аналогичными симптомами. Решается проблема точно также и для php-fpm (меняем ту же переменную).
Запрещаем виртуальному хосту отправлять почту
Иногда бывает такая ситуация, что надо наоборот запретить отправлять почту какому-то сайту. Например, если ясно, что на нём вирус, но на лечение вируса нужно время.
Тогда действуем от противного — в настройки виртуального хоста проблемного сайта добавляем параметр:
php_admin_value sendmail_path "/dev/null"
И перезапускаем Apache, чтобы изменения вступили в силу. Либо вы можете включить упомянутый выше параметр SELinux, однако, учтите, что это не поможет, если в вашей системе SELinux отсутствует или перманентно отключен.
19.11.2015 в 13:54
> Если версия php у вас поновее чем 5.3, то в лог добавятся дата и время (что, конечно, очень полезно).
У меня php 5.4, но дата и время в лог не добавляется! Есть решение?
05.04.2016 в 22:00
Leonid, а у меня PHP 5.5.32 пишет в лог даты. Запись выглядит примерно так:
[05-Apr-2016 15:22:28 Europe/Moscow] mail() on [/var/www/xxx/public/sites/... ...MailSystem.class.php:47]: To: xxx@xxx -- Headers: MIME-Version: 1.0 Content-Type: text/html; charset=UTF-8; Content-Transfer-Encoding: 8Bit X-Mailer: Drupal Return-Path: xxx@xxx Sender: xxx@xxx From: xxx@xxx Bcc: xxx@xxx
C какой точно версии даты начали писаться — не скажу. Пока только известно, что PHP 5.3.3 их не пишет. И ваш 5.4 — тоже. А 5.5.32 уже точно пишет.
Если вы не хотите обновлять php, то, пожалуй, единственное решение в том, чтоб написать свой скрипт-прокладку, который потом указать в sendmail_path. То есть php будет на этот скрипт передавать письма, скрипт будет их желаемым вами образом логировать, а потом уже отдавать в настоящий sendmail для реальной отправки.
Самый простой вариант скрипта-прокладки выглядит примерно так:
sendmail_path = "tee -a /var/log/phpmail.log | /usr/sbin/sendmail -t -i"
🙂Ремарка для новичков: это надо прописать в
/etc/php.ini
, предварительно создав файл/var/log/phpmail.log
с правами на запись.