Skip to content

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.

Koltrix Team5 min read
Colorful JavaScript code on a dark screen
Photo by Markus Spiske on Unsplash
On this page(8 sections)
  1. Why test by hand
  2. swaks: the Swiss Army knife for SMTP
  3. A basic transaction
  4. Testing STARTTLS and authentication
  5. Testing headers and content
  6. openssl s_client: seeing the TLS layer
  7. STARTTLS on submission or relay ports
  8. Implicit TLS
  9. Checking an MX's inbound TLS
  10. Checking which TLS versions a server accepts
  11. Reading what the server tells you
  12. A troubleshooting sequence
  13. Checklist
  14. 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-connect for implicit TLS, typically port 465.
  • --auth LOGIN or other mechanisms, to test what the server accepts.
  • --quit-after AUTH to test credentials without sending a message.
  • --quit-after RCPT to 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:

  1. 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.
  2. What does EHLO advertise? Run swaks with --quit-after EHLO and check for STARTTLS, AUTH mechanisms and SIZE.
  3. Does TLS work? Use openssl s_client with -starttls smtp and check the certificate.
  4. Do credentials work? swaks with --quit-after AUTH.
  5. Is the sender accepted? --quit-after MAIL. Many relays only accept From addresses you have registered.
  6. Is the recipient accepted? --quit-after RCPT.
  7. 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 smtp reveals 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.

SharePost on XLinkedIn
More in Guides →