Testing SMTP by hand with swaks and openssl
Talk to a mail server directly to see what it really says. Use swaks and openssl s_client to test auth, TLS, relaying and rejections step by step.

On this page(8 sections)
- Why test by hand
- swaks: the Swiss Army knife for SMTP
- A basic transaction
- Testing STARTTLS and authentication
- Testing headers and content
- openssl s_client: seeing the TLS layer
- STARTTLS on submission or relay ports
- Implicit TLS
- Checking an MX's inbound TLS
- Checking which TLS versions a server accepts
- Reading what the server tells you
- A troubleshooting sequence
- Checklist
- Key takeaways
When an application says "SMTP error" and nothing more, the quickest way to the truth is to talk to the mail server yourself. Two command-line tools cover almost every case: swaks for scripted SMTP conversations, and openssl s_client for seeing exactly what happens during TLS.
Why test by hand
Mail libraries hide the conversation. They report "authentication failed" or "connection refused" and discard the server's actual words, which usually contain the real answer: a specific enhanced status code, a policy message, or a hint about which extension is missing. Talking to the server directly shows you every reply line.
Use these tools against servers you operate or are authorized to test, such as your own relay, your provider's submission endpoint with your credentials, or a test mailbox you own. Sending test messages to arbitrary third-party servers can look like abuse.
swaks: the Swiss Army knife for SMTP
swaks (Swiss Army Knife for SMTP) is a single script available in most package managers. It runs a complete SMTP transaction and prints both sides.
A basic transaction
swaks --server mail.example.com --port 587 \
--from [email protected] --to [email protected] \
--header "Subject: swaks test" --body "Hello from swaks"
The output marks client lines with -> and server lines with <-, so you see the greeting banner, the EHLO response with advertised extensions, and each reply code.
Testing STARTTLS and authentication
Most submission servers on port 587 expect STARTTLS and then authentication:
swaks --server mail.example.com --port 587 --tls \
--auth PLAIN --auth-user [email protected] --auth-password "$SMTP_PASSWORD" \
--from [email protected] --to [email protected]
Useful variations:
--tls-on-connectfor implicit TLS, typically port 465.--auth LOGINor other mechanisms, to test what the server accepts.--quit-after AUTHto test credentials without sending a message.--quit-after RCPTto test whether a recipient would be accepted, without sending data.
Pass passwords through environment variables rather than typing them on the command line, where they end up in shell history.
Testing headers and content
swaks --server mail.example.com --port 587 --tls \
--auth PLAIN --auth-user [email protected] --auth-password "$SMTP_PASSWORD" \
--from [email protected] --to [email protected] \
--header "Subject: Header test" \
--header "List-Unsubscribe: <https://example.com/u/abc>" \
--header "List-Unsubscribe-Post: List-Unsubscribe=One-Click" \
--body @message-body.txt
--data @file.eml sends a complete prepared message, including your own headers and MIME structure, which is handy for reproducing a problem message exactly.
openssl s_client: seeing the TLS layer
When the problem is TLS, such as certificate errors, protocol mismatches or STARTTLS failures, openssl s_client shows the handshake in detail.
STARTTLS on submission or relay ports
openssl s_client -connect mail.example.com:587 -starttls smtp -servername mail.example.com
-starttls smtp makes openssl perform the plaintext EHLO and STARTTLS exchange before the handshake. The output shows the certificate chain, the negotiated protocol version and cipher, and verification results. Look for Verify return code: 0 (ok); anything else is a certificate problem.
Implicit TLS
openssl s_client -connect mail.example.com:465 -servername mail.example.com
Checking an MX's inbound TLS
dig +short MX example.net
openssl s_client -connect mx1.example.net:25 -starttls smtp -servername mx1.example.net </dev/null 2>/dev/null \
| openssl x509 -noout -subject -issuer -dates
This prints the certificate's subject, issuer and validity dates, which is useful when checking MTA-STS readiness or investigating a TLS-RPT report.
After the handshake, s_client leaves you in an interactive session where you can type SMTP commands, but it does not handle authentication encoding for you. Use swaks for full transactions and openssl for TLS inspection.
Checking which TLS versions a server accepts
When a connection fails only from certain clients, compare protocol versions explicitly:
openssl s_client -connect mail.example.com:587 -starttls smtp -tls1_2 </dev/null 2>/dev/null | grep -E "Protocol|Cipher"
openssl s_client -connect mail.example.com:587 -starttls smtp -tls1_3 </dev/null 2>/dev/null | grep -E "Protocol|Cipher"
An old application runtime that only speaks TLS 1.0 or 1.1 will fail against a server that has disabled those versions, and the error your application logs may say nothing more useful than "handshake failure." Testing each version separately settles the question in seconds.
Remember that a provider's documentation is the final word on which ports and TLS modes it supports. Some relays offer STARTTLS on one port and implicit TLS on another, and some terminate TLS elsewhere and decline STARTTLS on particular ports. If the documented settings and your test disagree, capture the full swaks output and send it to the provider's support team.
Reading what the server tells you
| Reply | Typical meaning | Next step |
|---|---|---|
220 banner, then nothing |
Connected; waiting for EHLO | Normal |
421 on connect |
Server busy or rate-limiting your IP | Wait and retry; check limits |
454 4.7.0 after STARTTLS |
TLS not available right now, or not offered on this port | Check the provider's documented port and TLS mode |
530 5.7.0 |
Authentication required | Authenticate before MAIL FROM |
535 5.7.8 |
Authentication credentials invalid | Check username, password or API key |
550 5.1.1 at RCPT |
Recipient does not exist | Fix the address |
550 5.7.1 |
Policy rejection: relaying denied, sender not allowed, or reputation | Read the full message text |
552 5.3.4 |
Message too large | Reduce size |
250 2.0.0 after DATA |
Accepted for delivery | Delivery is now the server's job |
The text after the code often names the exact cause, for example a sender address not registered on the account or a recipient limit. Copy it into your bug report.
A troubleshooting sequence
When an application cannot send through a relay:
- Can you connect?
nc -vz mail.example.com 587. Failure means a firewall, wrong host or wrong port. Many cloud providers block outbound port 25 by default. - What does EHLO advertise? Run swaks with
--quit-after EHLOand check for STARTTLS, AUTH mechanisms and SIZE. - Does TLS work? Use
openssl s_clientwith-starttls smtpand check the certificate. - Do credentials work? swaks with
--quit-after AUTH. - Is the sender accepted?
--quit-after MAIL. Many relays only accept From addresses you have registered. - Is the recipient accepted?
--quit-after RCPT. - Is the message accepted? Send a full test, then a copy of the problem message with
--data.
The first step that fails tells you where to look, and the server's reply usually tells you why.
Checklist
- Install swaks and confirm openssl is available on your debugging machine.
- Keep credentials in environment variables during tests.
- Step through connect, EHLO, TLS, AUTH, MAIL, RCPT and DATA, stopping at the first failure.
- Record the full reply text, not just the code.
- Test only against servers you operate or are authorized to use.
Key takeaways
- Mail libraries hide the SMTP conversation; swaks and openssl show you every line.
- swaks runs full or partial transactions, including STARTTLS, authentication and custom headers.
openssl s_client -starttls smtpreveals certificate and protocol problems.- Stepping through the transaction one stage at a time pinpoints exactly where sending fails.
Start with Koltrix
Your domain, one inbox, and an API that sends.
A team inbox where AI sorts and drafts (nothing is sent without your click), plus the transactional API and SMTP relay your product sends with. 7 days free, no card.

