Migrate to RunxBuild and earn up to $50 in hosting credit on your first deposit.

Calculate your savings
unxBuild
Back to Blog Explainer

Port 587: The SMTP Submission Port, and Why Your App Should Use It

Sean

Platform Writer

Aug 10, 2026
7 min read

Port 587 is the mail submission port: the port an application or mail client uses to hand an outgoing message to a mail server, with authentication required and TLS negotiated via STARTTLS. If your app sends email and you are choosing a port, this is the default and has been since RFC 2476 in 1998.

Port 587: The SMTP Submission Port, and Why Your App Should Use It

The confusion around SMTP ports is almost entirely historical. There are four numbers in circulation, two of them are deprecated in one direction or another, and the guidance you find depends on what year the page was written. Here is the current state, and the reasoning that produced it.

Table of contents

Submission versus relay, which is the whole distinction

SMTP does two jobs that look identical and are not. Relay is server-to-server: one mail server passing a message to the next on its way to the recipient. Submission is client-to-server: your application, or somebody’s mail client, handing a brand-new message to the first server in the chain.

For most of SMTP’s early life both happened on port 25, unauthenticated. That worked until spam, at which point the lack of any distinction became the problem — a compromised machine could inject mail anywhere with no credential.

RFC 2476 split them. Submission moved to port 587 with authentication required; relay stayed on 25. RFC 6409 later restated it in the language people still quote: port 587 is reserved for email message submission.

This is why sending on port 25 from an application is a mistake even when it works. Nearly every cloud provider and residential ISP blocks outbound 25 by default, precisely because legitimate applications should not be using it.

The four ports, and what each is for

  • Port 587 — submission, STARTTLS. The default. Connection opens in the clear, the client issues STARTTLS, the connection upgrades to TLS, then the client authenticates. Universally supported.
  • Port 465 — submission, implicit TLS. TLS is established before any SMTP conversation happens. Deprecated in 1998, un-deprecated by RFC 8314 in 2018, which now recommends it over 587 on security grounds. Widely supported in practice.
  • Port 25 — relay. Server-to-server delivery. Blocked outbound by most hosting providers and ISPs. Do not use it from application code.
  • Port 2525 — unofficial fallback. Not in any RFC. Offered by most email providers as an escape hatch for networks that block both 587 and 465.

The 587-versus-465 argument is smaller than the internet makes it sound. Implicit TLS on 465 has one real advantage: there is no plaintext phase to strip. A STARTTLS downgrade attack requires an active man-in-the-middle, which is rare but not theoretical.

Practical rule: use 587 unless your provider tells you to use 465, and enforce TLS in your client library either way. The failure mode that actually bites people is not port choice — it is a library configured to fall back to plaintext when STARTTLS fails.

Configuring it in application code

The pattern is the same in every language: host, port, credentials from the environment, TLS enforced.

import os, smtplib, ssl
from email.message import EmailMessage

msg = EmailMessage()
msg["From"] = "[email protected]"
msg["To"] = "[email protected]"
msg["Subject"] = "Password reset"
msg.set_content("Use the link below to reset your password.")

context = ssl.create_default_context()
with smtplib.SMTP(os.environ["SMTP_HOST"], 587, timeout=10) as s:
    s.ehlo()
    s.starttls(context=context)   # raises if the server will not upgrade
    s.login(os.environ["SMTP_USER"], os.environ["SMTP_PASS"])
    s.send_message(msg)
import nodemailer from 'nodemailer';

const transport = nodemailer.createTransport({
  host: process.env.SMTP_HOST,
  port: 587,
  secure: false,        // false for 587; true for implicit TLS on 465
  requireTLS: true,     // refuse to send if STARTTLS is unavailable
  auth: {
    user: process.env.SMTP_USER,
    pass: process.env.SMTP_PASS,
  },
});

await transport.sendMail({
  from: '[email protected]',
  to: '[email protected]',
  subject: 'Password reset',
  text: 'Use the link below to reset your password.',
});

requireTLS: true in Nodemailer and the exception from starttls() in Python are the important lines. Without them, a server that declines to upgrade gets your credentials in plaintext and your library reports success.

When port 587 is not the problem

A large share of email that fails to arrive was submitted perfectly on the correct port. Submission is the easy half. Delivery is where it goes wrong.

  • SPF — a DNS TXT record naming which servers may send for your domain. If you send through a provider and have not listed them, receiving servers see a mismatch.
  • DKIM — a cryptographic signature on the message, with the public key in DNS. Your provider gives you the record; you have to publish it.
  • DMARC — a policy record saying what to do when SPF and DKIM fail, plus an address to report to. Start at p=none and read the reports before tightening.
  • Reverse DNS and a warm IP — relevant if you run your own mail server, largely handled for you if you do not.

If mail is landing in spam, the port is not why. Check the three DNS records first; that is the fix in the overwhelming majority of cases.

It is also worth saying: running your own outbound mail server in 2026 is a choice, not a default. Deliverability is a reputation game played against systems you cannot inspect. Use a transactional email provider, point your app at 587, publish the DNS records they hand you, and spend the saved time elsewhere.

Egress, blocking, and what your platform allows

Before debugging your code, confirm the packet can leave. Outbound port blocking is common and the symptom is a connection timeout that looks exactly like a wrong hostname.

# Can we reach the submission port at all?
nc -vz smtp.example.com 587

# Watch the STARTTLS negotiation end to end
openssl s_client -starttls smtp -connect smtp.example.com:587 -crlf

# Compare against relay, which is usually blocked
nc -vz smtp.example.com 25

A timeout on 587 and success on 2525 is a blocked-port diagnosis, not a code one. Switch ports and move on.

On RunxBuild, application services make outbound connections normally, and the SMTP credentials belong in environment variables on the service rather than in the repository — the same place your database URL lives. The services documentation covers where those are set and how they reach the runtime.

How this fits the rest of the stack

Port 587 is the answer to the port question, and the port question is rarely the real one. Use 587 with enforced STARTTLS, keep credentials in environment variables, and treat SPF, DKIM, and DMARC as the actual deliverability work. If you are sizing an app that sends transactional mail and want the runtime, database, and bandwidth as separate numbers rather than one bundled figure, the RunxBuild hosting calculator breaks them out.

Useful related references:

FAQ

Is port 587 secure?

Yes, when TLS is enforced. Port 587 begins in plaintext and upgrades via STARTTLS, so security depends on your client refusing to continue if the upgrade fails. Set requireTLS in Nodemailer or let Python’s starttls() raise rather than catching it.

What is the difference between port 587 and port 465?

Both carry authenticated submission. Port 587 negotiates TLS via STARTTLS after connecting in the clear; port 465 establishes TLS before any SMTP conversation. RFC 8314 mildly prefers 465 because there is no plaintext phase to attack, but both are widely supported.

Why is port 25 blocked?

Port 25 is for server-to-server relay, and unauthenticated relay was the primary vector for spam. Cloud providers and ISPs block outbound 25 by default because legitimate applications should be submitting on 587 or 465 instead.

When should I use port 2525?

Only as a fallback. Port 2525 is not defined in any RFC but is offered by most email providers for networks that block 587 and 465. Functionally it is identical to 587.

My email sends on port 587 but goes to spam. What is wrong?

Almost certainly DNS, not the port. Publish an SPF record listing your sending provider, add the DKIM key they give you, and set up a DMARC record starting at p=none. Those three fix the large majority of spam-folder problems.

#port 587#smtp port#smtp submission#starttls#transactional email