STARTTLS is a command, not a protocol. It lets a connection that began in plain text upgrade itself to TLS partway through, on the same port, without either side knowing in advance that encryption was available. That design is what got most of the internet's email encrypted — and its one structural weakness is why MTA-STS, DANE and TLS-RPT all exist.
The Problem STARTTLS Solved
The obvious way to encrypt a protocol is to give it a second port where TLS starts immediately — https alongside http, port 465 alongside port 25. That is implicit TLS, and it works, but it requires every server, firewall and client to agree on a new port before anything can move.
STARTTLS took the other route. Keep the existing port, keep the existing plain-text handshake, and add one command that says "switch to TLS now". A server that understands it upgrades; a server that does not carries on in plain text. That backwards compatibility is why SMTP could be encrypted incrementally across millions of independently operated servers — and it is also the reason encryption is optional by default.
STARTTLS), POP3 (STLS), XMPP, LDAP and FTP (AUTH TLS). The mechanics and the trade-offs are identical everywhere: one port, an optional upgrade, and a plain-text window before it happens.How the Upgrade Works
The sequence is short. The client greets the server, reads the capability list, asks for the upgrade, and then starts the conversation over — because everything said before the handshake is untrusted and must be discarded.
S: 220 mail.example.com ESMTP ready
C: EHLO client.example.org
S: 250-mail.example.com
S: 250-STARTTLS <-- the offer
S: 250-SIZE 35882577
S: 250 8BITMIME
C: STARTTLS
S: 220 2.0.0 Ready to start TLS
... TLS handshake: certificate, cipher, key exchange ...
C: EHLO client.example.org <-- repeat, inside TLS
S: 250-mail.example.com
S: 250-AUTH PLAIN LOGIN <-- capabilities may differ now
S: 250 8BITMIMETwo details in that exchange matter more than they look:
- The second
EHLOis mandatory. The first capability list arrived in plain text and could have been tampered with, so the client throws it away and asks again over the encrypted channel. This is also whyAUTHcommonly appears only in the second list — servers refuse to advertise authentication until the connection is protected. - The offer itself is unauthenticated. The
250-STARTTLSline is sent before any certificate has been presented. Nothing about it is signed, and nothing proves it was not removed in transit.
Opportunistic by Default
On the server-to-server transport leg, STARTTLS is opportunistic: senders use it when it is offered and fall back to plain text when it is not. That is a deliberate choice — refusing to deliver to a server without TLS would have meant losing mail during the decade it took for adoption to become universal.
The consequence is that three quite different outcomes all look like "delivered":
| Situation | What happens | Message protected? |
|---|---|---|
| Server offers STARTTLS, handshake succeeds | Session encrypted | Yes, for this hop |
| Server does not offer STARTTLS | Sender delivers in cleartext | No |
| Offer present, handshake fails | Most senders retry without TLS | No |
| Offer stripped in transit | Sender never knew it was available | No, and nobody is told |
| Certificate invalid or self-signed | Usually accepted anyway on port 25 | Encrypted, but unauthenticated |
That last row is worth sitting with. On the transport leg most senders do not verify the certificate at all, because a strict check would reject a large share of legitimate mail servers. Opportunistic STARTTLS therefore protects against a passive observer recording traffic, and not against an active attacker who can modify it.
STARTTLS Stripping
The attack follows directly from the design. An attacker positioned between two servers watches the plain-text capability list go past and deletes or corrupts the 250-STARTTLS line — often by rewriting it to something the client will ignore, such as 250-XXXXXXXA, which keeps the line length identical and avoids breaking the session.
The sending server reads the modified list, concludes that TLS is unavailable, and delivers the message in the clear. No error is raised on either end. Nothing in the logs distinguishes it from a legitimate server that genuinely does not support TLS.
Closing the Gap
You cannot fix stripping inside STARTTLS itself, because the vulnerable moment happens before any trust has been established. Every solution therefore works the same way: publish a commitment out-of-band, so a sender knows TLS should have been available and can refuse to fall back.
| Mechanism | Published as | What it guarantees |
|---|---|---|
| MTA-STS | A DNS TXT record plus an HTTPS-hosted policy file | Senders must use TLS with a valid, matching certificate |
| DANE (DNSSEC + TLSA) | A signed TLSA record in DNS | Pins the exact certificate or key the server must present |
| TLS-RPT | A DNS TXT record naming a reporting address | Nothing on its own — it tells you when TLS failed |
| Implicit TLS (port 465) | Nothing; the port implies it | No plain-text phase exists to strip |
MTA-STS and DANE solve the same problem with different trust anchors: MTA-STS leans on the web PKI and HTTPS, DANE leans on DNSSEC. Publishing either one moves your domain from "encrypted when convenient" to "encrypted or not delivered". TLS-RPT is the companion to both — it is how you find out that a sender could not comply, instead of discovering it months later.
STARTTLS Versus Implicit TLS
For client submission the choice is straightforward, and the encryption you end up with is the same either way. What differs is whether a plain-text phase exists at all.
| STARTTLS | Implicit TLS | |
|---|---|---|
| Typical port | 587 (submission), 25 (transport) | 465 |
| Connection starts | Plain text, upgraded on request | Encrypted from the first byte |
| Downgradable | Yes, by an active attacker | No — there is no offer to remove |
| Fails when misconfigured | Falls back silently, or hangs | Connection refused or handshake error |
| Compatibility | Universal | Universal for submission; rare for transport |
Checking It Yourself
Everything above is observable from a terminal. Start with the capability list, then confirm the handshake actually completes and the certificate verifies:
# Is STARTTLS advertised on the transport port?
openssl s_client -connect mail.example.com:25 -starttls smtp -crlf </dev/null 2>&1 \
| grep -i 'starttls\|verify return'
# Submission port, with the certificate chain and verify result
openssl s_client -connect smtp.example.com:587 -starttls smtp -crlf </dev/null
# Implicit TLS — encrypted before the banner arrives
openssl s_client -connect smtp.example.com:465 -crlf </dev/null
# Which hosts accept mail for the domain, in preference order
dig +short MX example.comRead Verify return code: 0 (ok) as a clean chain. Anything else names the problem — 10 is an expired certificate, 18 a self-signed one, 21 a chain that could not be verified. On port 25 those are often tolerated by real senders; on 587 they will stop clients from connecting.
Common Misconceptions
- "STARTTLS is an older, weaker form of TLS." It is not a version of TLS at all — it is the command that starts one. The cipher suites and protocol versions are whatever the two ends negotiate.
- "Port 465 is deprecated." It was briefly, in the late 1990s, and was formally reinstated for message submission in 2018. Both 465 and 587 are current.
- "My mail is encrypted, so it is private." Only in transit, and only per hop. Every server along the path holds the message in plain text.
- "The server advertises STARTTLS, so TLS is working." The offer and a successful handshake are different things. Certificates expire; ciphers stop overlapping.
- "Enabling STARTTLS is enough." Without MTA-STS or DANE it stays optional, and an attacker can remove the option.
Frequently Asked Questions
1. Is STARTTLS less secure than regular TLS?
The encryption is identical — same TLS versions, same ciphers, same certificates. What differs is how you get there. Implicit TLS is encrypted from the first byte, so there is nothing to downgrade. STARTTLS begins in plain text and upgrades on request, which leaves a window where an active attacker can remove the offer. Once the handshake completes, the two are equivalent.
2. Should I use port 587 with STARTTLS or 465 with implicit TLS?
For client submission either is fine, and the encryption ends up the same. Port 465 is marginally simpler because TLS is never optional — there is no plain-text window at all. Port 587 is more widely documented and more likely to be what your provider expects. The mistake to avoid is mismatching them: implicit TLS on 587 or STARTTLS on 465 hangs until it times out.
3. What is a STARTTLS stripping attack?
An attacker sitting between two mail servers deletes the 250-STARTTLS line from the plain-text capability list. The sender never learns the upgrade was available, falls back to cleartext, and delivers the message unencrypted. Neither side sees an error. MTA-STS and DANE exist to close exactly this gap by telling senders that TLS is mandatory for your domain.
4. Does STARTTLS mean my email is end-to-end encrypted?
No. It protects one hop between two servers. The message is decrypted on arrival, sits in plain text on that server, and is re-encrypted for the next hop only if that hop also offers TLS. Anyone with access to a server on the path reads the message. End-to-end encryption is PGP or S/MIME, which operates on the message body rather than the connection.
5. Why does my server show STARTTLS in EHLO but the handshake still fails?
Advertising the capability only means the server will accept the command. The handshake can still fail on an expired or wrong-hostname certificate, an untrusted chain, a TLS version the client refuses, or no shared cipher suite. Run openssl s_client -connect host:587 -starttls smtp and read the verify return code — it names the certificate problem directly.
6. How do I check whether my mail server offers STARTTLS?
Run the host through the SMTP Test — it shows the EHLO capability list, whether STARTTLS is advertised, and whether the upgrade actually succeeds. By hand, connect with openssl s_client -connect host:25 -starttls smtp -crlf and look for 250-STARTTLS in the response.