Rails отправляет почту через Action Mailer, которому достаточно одного хеша smtp_settings. Ниже — настройка fmailer, классы мейлеров, HTML- и текстовые вьюхи, вложения и вынос доставки из запроса через deliver_later.
| Параметр | Значение |
|---|---|
| SMTP-хост | smtp.fmailer.ru |
| Порт | 587 |
| Шифрование | STARTTLS (соединение переводится в TLS до авторизации) |
| Логин | Логин SMTP-токена вашего домена |
| Пароль | Пароль этого токена — показывается один раз, при создании |
# config/environments/production.rb
config.action_mailer.delivery_method = :smtp
config.action_mailer.perform_deliveries = true
config.action_mailer.raise_delivery_errors = true # не проглатывать ошибки
config.action_mailer.default_url_options = { host: "example.ru" }
config.action_mailer.smtp_settings = {
address: "smtp.fmailer.ru",
port: 587,
user_name: Rails.application.credentials.dig(:smtp, :user),
password: Rails.application.credentials.dig(:smtp, :password),
authentication: :plain,
enable_starttls_auto: true,
open_timeout: 10,
read_timeout: 10,
}Для порта 465 поменяйте три параметра транспорта:
# Порт 465 — шифрование сразу, без STARTTLS
port: 465,
ssl: true,
enable_starttls_auto: false,Доступы держите в зашифрованных credentials Rails:
# EDITOR=vim bin/rails credentials:edit
smtp:
user: <логин токена>
password: <пароль токена>false, то есть Action Mailer молча проглатывает ошибки SMTP, и полностью сломанная конфигурация выглядит как рабочая. Это главная причина жалоб «Rails пишет, что отправил, а письма нет». bin/rails generate mailer OrderMailer confirmed# app/mailers/application_mailer.rb
class ApplicationMailer < ActionMailer::Base
default from: "Магазин <noreply@mail.example.ru>"
layout "mailer"
end
# app/mailers/order_mailer.rb
class OrderMailer < ApplicationMailer
def confirmed(order)
@order = order
mail(to: order.customer.email, subject: "Подтверждение заказа №#{order.id}")
end
end Делайте для каждого мейлера две вьюхи — HTML и текстовую. Rails найдёт пару по имени файла и соберёт multipart/alternative сам. HTML без текстовой части почтовые фильтры считают признаком спама:
<%# app/views/order_mailer/confirmed.html.erb %>
<h1>Заказ принят</h1>
<p><%= @order.customer.name %>, доставка — завтра до 18:00.</p>
<%# app/views/order_mailer/confirmed.text.erb %>
Заказ принят
<%= @order.customer.name %>, доставка — завтра до 18:00.# Блокирует запрос на время обмена с SMTP.
OrderMailer.confirmed(@order).deliver_now
# Через Active Job — ответ уходит сразу, при сбое будет повтор.
OrderMailer.confirmed(@order).deliver_later
# Или с задержкой
OrderMailer.confirmed(@order).deliver_later(wait: 10.minutes) Во всём, что вызывается из запроса, используйте deliver_later. deliver_now держит ответ открытым на всё время обмена с SMTP и превращает кратковременный сбой релея в ошибку 500 для пользователя.
class InvoiceMailer < ApplicationMailer
def monthly(invoice)
attachments["invoice.pdf"] = File.read(invoice.path)
# Картинка в теле письма — в вьюхе как attachments["logo.png"].url
attachments.inline["logo.png"] = File.read(Rails.root.join("app/assets/images/logo.png"))
mail(to: invoice.customer.email, subject: "Счёт на оплату")
end
end Массив в to: кладёт все адреса в один заголовок To:, где получатели видят друг друга. Отправляйте циклом:
# Отдельное письмо каждому — никто не видит чужих адресов.
User.find_each do |user|
ReportMailer.weekly(user).deliver_later
end# config/environments/development.rb
config.action_mailer.delivery_method = :letter_opener # открывает письма в браузере
# или просто
config.action_mailer.perform_deliveries = false# test/mailers/order_mailer_test.rb
test "письмо о заказе отправляется" do
assert_emails 1 do
OrderMailer.confirmed(orders(:one)).deliver_now
end
mail = ActionMailer::Base.deliveries.last
assert_equal ["client@example.ru"], mail.to
end| Ошибка | Причина и решение |
|---|---|
| Ошибок нет, писем тоже | raise_delivery_errors равен false. Включите его. |
Net::SMTPAuthenticationError: 535 | Неверные user_name или password. |
Net::OpenTimeout | Порт заблокирован. Используйте 8587 или 8465. |
OpenSSL::SSL::SSLError: wrong version number | ssl: true на порту 587 или STARTTLS на 465. |
Ссылки ведут на localhost:3000 | Не задан default_url_options[:host] для этого окружения. |
Net::SMTPFatalError: 550 | Домен отправителя не подтверждён. |
Вне окружения development параметр raise_delivery_errors по умолчанию равен false, и Action Mailer проглатывает все ошибки SMTP. Поставьте config.action_mailer.raise_delivery_errors = true — и настоящая причина (авторизация, таймаут, отклонённый отправитель) станет видна сразу.
На порту 587 нужен enable_starttls_auto: true — соединение открывается незашифрованным и переводится в TLS до авторизации. На порту 465 нужен ssl: true вместе с enable_starttls_auto: false, потому что там шифрование начинается с первого байта. Смешение этих вариантов приводит к ошибке рукопожатия.
Неверные user_name или password. В user_name нужен полный логин SMTP-токена домена, в password — значение, показанное один раз при создании токена. Храните оба в Rails credentials, а не в файле окружения.
deliver_now открывает SMTP-соединение прямо в текущем запросе: пользователь ждёт, а временный сбой превращается в ошибку 500. deliver_later ставит задачу Active Job, которую выполняет воркер, — запрос остаётся быстрым, а неудачная отправка повторяется. Везде, кроме скриптов и тестов, используйте deliver_later.
У вьюх мейлера нет контекста запроса, поэтому хелперы *_url берут хост из default_url_options. Задайте config.action_mailer.default_url_options = { host: 'example.ru' } в каждом окружении.
Многие хостинги блокируют исходящие порты 25, 465 и 587. Поменяйте порт в smtp_settings на 8587 для STARTTLS или 8465 для шифрования с первого байта.