Short answer
535 5.7.8 means the mail server refused your SMTP login. Your app connected and started the AUTH step, and the server said the username and password it got were not acceptable. No message was sent and none will be retried, because a 5xx reply is permanent.
The fix is almost always in your own config: the username, an app password, an admin policy, an environment variable or the TLS settings. Test the login by hand before you redeploy.
What 535 5.7.8 means
The full reply usually looks like one of these:
535-5.7.8 Username and Password not accepted. For more information, go to
535 5.7.8 https://support.google.com/mail/?p=BadCredentials ... - gsmtp
535 5.7.8 Error: authentication failedThe first is Gmail and Google Workspace. The second is the generic form from Postfix-style servers and many hosting providers. The steps below are in the order that finds the problem fastest.
The 535 reply code is the SMTP server's answer to a failed AUTH command. The 5.7.8 enhanced status code narrows that to "authentication credentials invalid." The rejection happens before any message content moves. Your recipient, your HTML and your domain's DNS have nothing to do with it.
That matters for triage. A 550 5.7.1 or a 550 5.7.26 is a delivery decision about your mail. A 535 5.7.8 is a login decision about your account. If you are working through a different code, our index of SMTP error codes covers each one.
Three causes account for most of these errors:
- Gmail and Google Workspace reject the normal account password for SMTP and expect an app password.
- Microsoft 365 returns 535 when SMTP AUTH is turned off for the tenant or the mailbox, even when the password is correct.
- Wrong credentials cause the rest, and the wrong value is often not the one you typed. It is the one your app actually read at runtime.
Check the username: use the full Gmail address
For Gmail and Workspace, the SMTP username is the full address: you@gmail.com or info@yourcompany.com. A bare local part like info will fail.
If you send as an alias, the login still uses the mailbox that owns the alias. The From header can be the alias, but auth.user must be the real account. Other servers vary. Some want the full address and some want only the local part, so check your provider's settings page before guessing.
Use an app password, not your account password
Google does not accept your regular account password over SMTP. You need an app password, and app passwords only exist on accounts with 2-Step Verification turned on.
To create one:
- Sign in to the Google account and open the Security settings.
- Under "Signing in to Google," turn on 2-Step Verification if it is off.
- Open App passwords and generate a new one.
- Use that generated password in your SMTP config in place of the account password.
Two details trip people up.
- Changing your Google password revokes app passwords. If mail worked for months and stopped on a particular day, a password change or a security event on the account is a likely cause. Generate a fresh app password.
- Google displays the app password in groups separated by spaces. Paste it without the spaces. In a nodemailer issue on GitHub, the original poster reported trying it both with and without spaces. Rule this out early.
Google Workspace: check what your admin allows
On a Workspace account, your admin's settings decide what you can do. If App passwords is missing from your security page, or a new one still fails, the organization may have blocked app passwords or SMTP access for your account. You cannot fix that from your own settings. Ask the admin what is allowed for the account you are sending from.
Microsoft 365 behaves the same way. SMTP AUTH can be disabled for the whole tenant or for one mailbox, and a disabled mailbox answers a correct password with 535. The admin has to turn SMTP AUTH on for that mailbox, or the app has to move to a different sending method.
Check how your app reads the credentials
A large share of 535 errors come from code that sends a different string than the one you think you set. Check what the process actually has at runtime.
- Trailing whitespace and newlines. A secret pasted into a
.envfile or a CI variable can carry a trailing space or\n. Trim it, or printJSON.stringify(process.env.SMTP_PASS)once in a debug build to see the exact characters, then remove the print. - Quotes.
SMTP_PASS="abcd efgh"in one loader becomesabcd efghand in another becomes"abcd efgh"with the quotes. Know which loader you use. - Unset variables with fallbacks. Code like
pass: process.env.APP_PASSWORD || ''sends an empty password when the variable is missing on the server. The login fails with 535 rather than with an error telling you the variable is missing. Fail at startup instead:
const user = process.env.SMTP_USER;
const pass = process.env.SMTP_PASS?.trim();
if (!user || !pass) throw new Error("SMTP_USER or SMTP_PASS is not set");- Local works, production fails. This is the most common shape in the nodemailer thread. One commenter's code worked on their machine and failed on an AWS EC2 instance. If the same credential works locally, compare the environment variables on the server first. They are usually different.
The same thread has a harder variant. The original poster said a new server with identical code and credentials could send, while the old server kept getting the password error, and Google's support told them everything was fine on Google's side. When the credentials and config are identical and only the host differs, the problem is no longer in your code. The cause is likely on the provider's side, where you cannot inspect it. We come back to this below.
Check the port and TLS settings: 587 with STARTTLS
Most servers only advertise AUTH after the connection is encrypted. Use one of these two pairings and don't mix them:
| Port | TLS mode | Nodemailer setting |
|---|---|---|
| 587 | STARTTLS (upgrade after connect) | port: 587, secure: false |
| 465 | Implicit TLS from the first byte | port: 465, secure: true |
Port 587 with secure: true, or 465 with secure: false, fails before or during the handshake. Our guide to SMTP ports explains when each port applies.
While you debug, remove tls: { rejectUnauthorized: false }. Both configs in the nodemailer issue had it set. It does not fix authentication, and it hides certificate problems that tell you whether you reached the right host.
Test the login outside your app before you redeploy
Don't push another config change and wait for a failed reset email. Test the login by hand. You can use our SMTP tester in the browser, or run this from the server that is failing:
openssl s_client -connect smtp.gmail.com:587 -starttls smtp -crlf -quietThen type:
EHLO test.localRead the 250-AUTH line in the reply. It lists the mechanisms the server accepts, such as LOGIN PLAIN XOAUTH2. Then authenticate with base64-encoded credentials:
printf '%s' 'you@gmail.com' | base64
printf '%s' 'yourapppassword' | base64AUTH LOGIN
<base64 username>
<base64 password>The reply tells you which of three cases you are in:
235 2.7.0Accepted. The credential and host are fine, and the bug is in how your app builds its config. Go back to the environment variable checks.535 5.7.8from your laptop and the server. The credential itself is wrong, or the account is not allowed to use SMTP.535 5.7.8only from the server. If the credential really is identical, the provider may be treating that host differently, and a config change may not fix it.
Why a Gmail login is a weak sender for password resets and sign-in codes
A Gmail or Workspace mailbox is built for a person reading and writing mail. When an app uses it to send password resets, receipts and sign-in codes, every failure mode above sits between your user and the code they are waiting for:
- A password change silently breaks sending.
- An admin policy change breaks it with no notice.
- One server can get 535 while another works with the same credentials.
Support may not be able to help with that last one. In the nodemailer issue, the original poster said Google's support told them everything was fine on Google's side.
Transactional mail is better sent through a relay built for it, where the credential belongs to an application, not a person, and the people running the servers can read the logs for your connection. If a reset email is also landing in spam once it does send, our password reset email debugging checklist covers the delivery side.
Send the same mail through Groundmail's SMTP relay
Groundmail sends transactional email from mail servers and IP space it operates itself. Marketing and cold email are not allowed on its shared pool. Its SMTP relay takes the same nodemailer, PHPMailer or framework mailer config you already have. Only the host and the credential change.
| Setting | Gmail login | Groundmail SMTP relay |
|---|---|---|
| Host | smtp.gmail.com | smtp.groundmail.dev |
| Port | 587 | 587 |
| TLS | STARTTLS (secure: false) | STARTTLS (secure: false) |
| Username | Your full Gmail or Workspace address | groundmail, always |
| Password | An app password, which requires 2-Step Verification | An SMTP credential from your Groundmail dashboard (gmsmtp_…) |
| Tied to | A person's mailbox and its security settings | An application credential you create and revoke |
const transporter = nodemailer.createTransport({
host: "smtp.groundmail.dev",
port: 587,
secure: false, // STARTTLS
auth: {
user: "groundmail",
pass: process.env.GROUNDMAIL_SMTP_PASS,
},
});Verify your domain, send a first test and go live
- Verify your sending domain so mail goes out with your own SPF, DKIM and DMARC alignment. The From address has to be on a verified domain, or the relay refuses it with a 554.
- Create an SMTP credential in the dashboard and put it in your environment variables, with the startup check from earlier.
- Send one message to your account email from an address on that domain and confirm it arrives.
- Add a card, which you need to send to anyone else even on the free plan, and switch production traffic over.
The free plan covers 3,000 emails a month, 100 a day and 3 domains. Pay as you go is $15 a month with 20,000 emails included. The pricing page has the per-1,000 rates above that.
Start sending free
If your reset and sign-in emails depend on a mailbox login that can return 535 without warning, move them to a relay built for that mail. You can create your free Groundmail account and send your first test from smtp.groundmail.dev today.
Sources
Facts on this page about other companies' products, prices and policies come from these public pages. They change, so check them before you decide.