A practical guide to testing an email server by hand. Learn what every SMTP command does, how the server answers, and how to run a full verification session with telnet or openssl.
SMTP (Simple Mail Transfer Protocol) is a plain-text, line-based protocol. That means you can talk to a mail server directly from a terminal and read exactly what it answers — no special software required. This is the most transparent way to check whether a mailbox exists, whether a server is reachable, and how it is configured.
First find the mail server (MX host) for the domain, then open a TCP connection to its SMTP port. The classic port is 25. Submission ports 587 and 2525 are used with STARTTLS; port 465 is implicit TLS (SMTPS).
Plain connection (port 25):
telnet mail.example.com 25
STARTTLS port (587 or 25) — telnet can't do TLS, use openssl:
openssl s_client -connect mail.example.com:587 -starttls smtp -crlf
Implicit TLS port (465):
openssl s_client -connect mail.example.com:465 -crlf
Note: many home and mobile ISPs block outgoing port 25 to fight spam. If telnet ... 25 just hangs, that is usually your provider, not the mail server. Test from a server/VPS where port 25 is open, or use submission ports.
You type commands (case-insensitive); the server replies with a 3-digit code and text. End every command with Enter (CRLF).
| Command | What it does |
|---|---|
HELO domain | Legacy greeting. Identifies the connecting host by name. The server replies 250. |
EHLO domain | Modern greeting (Extended HELO). Same as HELO but the server also lists supported extensions (STARTTLS, AUTH, SIZE, PIPELINING…). |
MAIL FROM:<addr> | Starts a new message and sets the envelope sender (the “return path”). Reply 250 if accepted. |
RCPT TO:<addr> | The key command for verification. Asks the server to accept a recipient. 250 = the mailbox is accepted; 550 = no such user; 4xx = try later (e.g. greylisting). |
DATA | Begins the message body. Server replies 354; you send headers + body and finish with a single dot . on its own line. (For a verification check you never reach this step.) |
RSET | Resets the current transaction (forgets MAIL FROM / RCPT TO) without closing the connection. |
VRFY user | Asks the server to verify a username/address. Designed for exactly this, but almost always disabled today (returns 252 or 502) because it leaks valid addresses to spammers. |
EXPN list | Asks the server to expand a mailing list into its members. Like VRFY, disabled almost everywhere for the same privacy reason. |
STARTTLS | Upgrades a plain connection to encrypted TLS (on ports 25/587). Offered only if listed in the EHLO reply. |
AUTH LOGIN / PLAIN | Authenticates a user (needed to send mail through submission ports, not for verification). |
NOOP | Does nothing; server replies 250. Useful to keep a connection alive or test responsiveness. |
HELP | Asks the server to list the commands it supports. |
QUIT | Politely ends the session. Server replies 221 and closes the connection. |
The first digit tells you the category, so you can read any reply even if you don't know the exact code:
354 after DATA).| Code | Meaning |
|---|---|
220 | Service ready (the greeting banner). |
221 | Closing connection (reply to QUIT). |
250 | OK — command accepted (the answer you want after RCPT TO). |
251 / 252 | User not local but will be forwarded / cannot verify but will try to deliver. |
354 | Start sending the message body (after DATA). |
421 | Service not available, closing — often rate-limiting. |
450 / 451 / 452 | Temporary problem (mailbox busy, local error, low storage). Greylisting frequently shows up here. |
502 | Command not implemented (typical reply to VRFY / EXPN). |
550 | Mailbox unavailable / no such user — the address does not exist. |
551 / 552 / 553 / 554 | User not local / over quota / bad mailbox name / transaction failed. |
Checking whether john@example.com exists. Lines you type are marked >; everything else is the server:
$ telnet mail.example.com 25 220 mail.example.com ESMTP ready > EHLO checkemail.test 250-mail.example.com Hello checkemail.test 250-STARTTLS 250-SIZE 52428800 250 PIPELINING > MAIL FROM:<probe@checkemail.test> 250 2.1.0 Ok > RCPT TO:<john@example.com> 250 2.1.5 Ok ← mailbox EXISTS > RSET 250 2.0.0 Ok > QUIT 221 2.0.0 Bye
If instead the recipient does not exist you would see something like:
> RCPT TO:<nosuchuser@example.com> 550 5.1.1 <nosuchuser@example.com>: Recipient address rejected: User unknown
4xx on the first attempt on purpose, asking you to come back later. A 4xx does not mean the address is invalid.250 and sort it out later. Then RCPT TO can't prove a specific mailbox exists. To detect a catch-all, try a clearly random address: if it is also accepted, the domain is catch-all.Want this done automatically, including MX lookup, catch-all detection and a clean verdict? Use our Email Checker on the home page.