Firebase Authentication emails users to verify their address, reset a password, confirm an email change and flag a newly added second factor. By default those messages come from a firebaseapp.com address and are sent by Google. Custom SMTP makes them come from your own domain, signed with its DKIM key — which is what gets them out of spam and makes them look like they come from you.
| Setting | Value |
|---|---|
| SMTP host | smtp.postwing.app |
| Port | 587 |
| Encryption | STARTTLS (the connection is upgraded to TLS before login) |
| Username | The login of an SMTP token for your domain |
| Password | The password of that token — shown once, when the token is created |
In the Firebase console, open Authentication → Templates, choose SMTP settings, enable it and fill in:
| Field | Value |
|---|---|
| Sender address | noreply@your-domain.com — on your verified domain |
| SMTP server host | smtp.postwing.app |
| SMTP server port | 587 |
| SMTP account username | The login of an SMTP token for your domain |
| SMTP account password | That token's password |
| SMTP security mode | STARTTLS |
587; SSL goes with port 465. Any other combination fails before the login is even tried. Still on the Templates tab, open each email type with the pencil icon. You can set the sender name, reply-to address and subject for all of them, and the message body for password reset. The placeholders %LINK%, %EMAIL%, %NEW_EMAIL%, %APP_NAME% and %DISPLAY_NAME% are filled in per message.
%APP_NAME% is the project's public-facing name from project settings. Make it the name users know you by — a verification email from "project-1234" reads like phishing.
The same setting through the Identity Toolkit admin API, for scripted or repeatable setups:
PROJECT_ID=your-project-id
curl -X PATCH \
"https://identitytoolkit.googleapis.com/admin/v2/projects/$PROJECT_ID/config?updateMask=notification.sendEmail.method,notification.sendEmail.smtp" \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
-H "X-Goog-User-Project: $PROJECT_ID" \
-H "Content-Type: application/json" \
-d '{
"notification": {
"sendEmail": {
"method": "CUSTOM_SMTP",
"smtp": {
"senderEmail": "noreply@your-domain.com",
"host": "smtp.postwing.app",
"port": 587,
"username": "token-login@your-domain.com",
"password": "your-token-password",
"securityMode": "START_TLS"
}
}
}
}'When the template editor is not enough — your own layout, your own language per user, the email sent from your backend — let the Admin SDK generate the action link and send the message yourself:
// Node.js backend or Cloud Function: your own email around Firebase's action link
import { initializeApp } from "firebase-admin/app";
import { getAuth } from "firebase-admin/auth";
import nodemailer from "nodemailer";
initializeApp();
const transporter = nodemailer.createTransport({
host: "smtp.postwing.app",
port: 587,
secure: false, // STARTTLS
auth: { user: process.env.SMTP_USER, pass: process.env.SMTP_PASS },
});
export async function sendPasswordReset(email) {
const link = await getAuth().generatePasswordResetLink(email, {
url: "https://your-domain.com/login", // where the user lands afterwards
});
await transporter.sendMail({
from: "Acme <noreply@your-domain.com>",
to: email,
subject: "Reset your Acme password",
text: "Reset your password: " + link,
html: '<p><a href="' + link + '">Reset your password</a></p>',
});
}generateEmailVerificationLink and generateSignInWithEmailLink work the same way.
For app emails triggered from Firestore, Firebase's Trigger Email from Firestore extension sends every document added to a collection through SMTP. Leave the password out of the connection URI and put it in the separate password field. The login is an email address, so its @ is written as %40:
# Trigger Email from Firestore extension
SMTP connection URI: smtp://token-login%40your-domain.com@smtp.postwing.app:587
SMTP password: your-token-password
Default FROM address: Acme <noreply@your-domain.com>Trigger a password reset for a test account from your app, or from the user list under Authentication → Users. Check the received message's From address and headers: it should show your domain, with DKIM passing.
| Symptom | Cause and fix |
|---|---|
Emails still come from firebaseapp.com | SMTP settings are not enabled, or were not saved. |
| Emails stop arriving after enabling SMTP | Wrong credentials, or the security mode does not match the port. |
| Verification email body cannot be edited | Firebase allows it only for password reset. Send your own email with an Admin SDK link. |
| Cloud Function times out connecting | Port 25 is blocked on Google Cloud. Use 587 or 465. |
| Mail goes to spam | Sender address is not on your verified domain. |
Yes. In the Firebase console, Authentication → Templates has an SMTP settings section: enable it and enter the sender address, host, port, username, password and security mode. From then on, Firebase's account emails are sent through your server instead of Google's default sender.
The account management emails on the Templates tab: email address verification, password reset, email address change and the notice sent when a second factor is added. SMS messages are unaffected, and so is anything your own code sends.
STARTTLS with port 587, or SSL with port 465. A mismatched pair fails the handshake before authentication even starts.
Only partly. Firebase lets you edit the sender name, reply-to and subject of every template, but the message body only for password reset — a restriction meant to keep the templates from being used for spam. For full control, generate the action link with the Admin SDK and send your own email, as shown above.
Custom SMTP changes who sends the email, not where its links go. The links lead to Firebase's default action handler. To use your own page, set a custom action URL in the template editor and handle the action codes there.
Yes, on port 587 or 465 — Google Cloud blocks outbound port 25, so never use that one. Cloud Functions can also call the REST API over HTTPS, which avoids SMTP entirely.