2026年11月14日

2026年11月14日

How to Test Email Sending with telnet, curl and swaks (SMTP Check)

This article is a localized edition of the Japanese original. Read the Japanese original

When mail from a WordPress contact form does not arrive, or you have just set up a mail server, connecting to the server directly over SMTP to check "does it accept the message?" makes it much easier to isolate the cause.

This article explains how to check with the following three tools.

Tool Best for
telnet / nc Typing the SMTP dialog line by line to see where it stops (no encryption)
curl Sending a prepared message file in one command, with STARTTLS or authentication
swaks A dedicated SMTP testing tool. TLS, auth, headers and attachments are set by arguments, and the whole dialog is shown

The commands and replies were checked as of October 2026 with Postfix 3.10, curl 8.14.1 and swaks 20240103.0 on Debian 13. Replace the server name mail.example.com and the addresses with your own.

Preparation: ports and installation

These are the three ports commonly used for SMTP.

Port Purpose Encryption
25 Mail delivery between servers Switched with STARTTLS after connecting (optional)
587 Sending from mail clients and apps (submission). Authentication is usually required Switched with STARTTLS after connecting
465 Same purpose as 587 TLS from the start of the connection

From home connections and cloud servers, outbound connections to port 25 are often blocked by the provider (Outbound Port 25 Blocking, OP25B). If you cannot connect to port 25, try 587 or 465 first.

Install the tools as follows.

# Debian / Ubuntu
sudo apt install telnet netcat-openbsd curl swaks

# RHEL / AlmaLinux / Rocky Linux (swaks is in EPEL)
sudo dnf install telnet nmap-ncat curl
sudo dnf install epel-release && sudo dnf install swaks

Talk to the server by hand with telnet

Connect to port 25 of the server with telnet and type SMTP commands one line at a time. Lines starting with > are what you type; the rest are server replies (do not type the > itself).

$ telnet mail.example.com 25
220 mail.example.com ESMTP Postfix (Debian)
> EHLO client.example.com
250-mail.example.com
250-PIPELINING
250-SIZE 10240000
250-STARTTLS
250-ENHANCEDSTATUSCODES
250-8BITMIME
250 CHUNKING
> MAIL FROM:<test@example.com>
250 2.1.0 Ok
> RCPT TO:<user@example.com>
250 2.1.5 Ok
> DATA
354 End data with <CR><LF>.<CR><LF>
> Subject: telnet test
>
> hello
> .
250 2.0.0 Ok: queued as A4D8E35FE9
> QUIT
221 2.0.0 Bye
Command Meaning
EHLO name Greeting. The 250- lines in the reply list the features the server supports (STARTTLS, AUTH and so on)
MAIL FROM:<address> The sender (envelope sender)
RCPT TO:<address> The recipient. Rejections often happen here
DATA Starts the message. Write headers, a blank line and the body, and end with a line containing only .
QUIT Disconnects

If you finally get 250 2.0.0 Ok: queued as ..., the server has accepted the message. The string after queued as is the queue ID, which helps you find the message in the server logs.

If telnet is not installed, nc mail.example.com 25 works the same way. Neither encrypts the connection, so do not use them to test password authentication (AUTH). To check an encrypted connection by hand, use openssl.

# Connect to port 587, encrypt with STARTTLS, then accept SMTP input
openssl s_client -starttls smtp -connect mail.example.com:587 -crlf -quiet

Send a message file with curl

curl also supports SMTP. First, prepare a message file mail.txt with headers and a body (a blank line between the headers and the body).

From: test@example.com
To: user@example.com
Subject: curl test

This is a test message sent with curl.

To send to port 25 without authentication:

curl --url smtp://mail.example.com:25 \
  --mail-from test@example.com \
  --mail-rcpt user@example.com \
  --upload-file mail.txt

To use STARTTLS and authentication on port 587, add --ssl-reqd and --user. If you give --user only a user name, curl prompts for the password (so the password does not end up in your shell history).

curl --url smtp://mail.example.com:587 --ssl-reqd \
  --user 'test@example.com' \
  --mail-from test@example.com \
  --mail-rcpt user@example.com \
  --upload-file mail.txt

For port 465, change the URL to smtps://mail.example.com:465.

On success, nothing is printed and the exit code is 0. Add -v to see the entire SMTP dialog. On failure, you see an error like the following.

Output Common cause
curl: (7) Failed to connect to ... The port is closed, or blocked by a firewall or OP25B
curl: (55) RCPT failed: 550 The recipient does not exist, or relaying is not allowed
curl: (67) ... (authentication failure) Wrong user name or password, or the authentication method does not match
curl: (60) SSL certificate problem: ... The server certificate cannot be verified (self-signed, host name mismatch and so on)

On a test server with a self-signed certificate, -k skips verification. Try production servers without it.

Test with swaks

swaks (Swiss Army Knife for SMTP) is a dedicated SMTP testing tool. It builds a test message from arguments alone, so you do not need a message file.

swaks --to user@example.com --from test@example.com \
  --server mail.example.com \
  --header "Subject: swaks test" \
  --body "This is a test message sent with swaks."

It prints the whole dialog. -> is what it sent and <- is the server's reply.

=== Trying mail.example.com:25...
=== Connected to mail.example.com.
<-  220 mail.example.com ESMTP Postfix (Debian)
 -> EHLO client.example.com
<-  250-mail.example.com
...
 -> MAIL FROM:<test@example.com>
<-  250 2.1.0 Ok
 -> RCPT TO:<user@example.com>
<-  250 2.1.5 Ok
 -> DATA
<-  354 End data with <CR><LF>.<CR><LF>
...
<-  250 2.0.0 Ok: queued as C696B3ABED
 -> QUIT
<-  221 2.0.0 Bye
=== Connection closed with remote host.

Common options

Option Meaning
--server host Server to connect to
--port number Port (25 if omitted)
--tls Require STARTTLS; exit if it is not available
--tls-on-connect Connect with TLS from the start. The default port becomes 465
--auth LOGIN Require authentication (PLAIN, CRAM-MD5 and others can also be given; if omitted, the methods the server offers are tried in order)
--auth-user / --auth-password User name and password for authentication. If you omit the password, you are prompted for it
--header "Name: value" Adds a header
--body "text" Sets the body
--attach file Attaches a file
--quit-after RCPT Disconnects at the given stage (no message is sent)

An example using STARTTLS and authentication on port 587:

swaks --to user@example.com --from test@example.com \
  --server mail.example.com --port 587 \
  --tls --auth LOGIN --auth-user test@example.com

When you run with authentication, the printed dialog includes the user name and password encoded in Base64. Base64 is trivially reversible, so do not send that output to others as-is or paste it anywhere public.

Check a recipient without sending

With --quit-after RCPT, swaks disconnects right after specifying the recipient and sends no message. Use it when you only want to know whether a recipient is accepted.

swaks --to nosuchuser@example.com --from test@example.com \
  --server mail.example.com --quit-after RCPT

A recipient that does not exist is rejected like this.

<** 550 5.1.1 <nosuchuser@example.com>: Recipient address rejected: User unknown in local recipient table

Reading reply codes

The first three digits of an SMTP reply tell you the result.

First digit Meaning Examples
2xx Success 250 2.0.0 Ok: queued as ...
3xx Waiting for more input 354 End data with <CR><LF>.<CR><LF>
4xx Temporary error. The message is retried later 451 4.3.0, 421
5xx Permanent error. Retrying fails again 550 5.1.1 User unknown, 554 5.7.1 Relay access denied, 535 5.7.8 Authentication failed

Relay access denied often appears when you try, without authenticating, to send to a domain the server does not handle. Authenticate on port 587 before sending, or review the server's relay settings.

Check the server logs

If the test message was "accepted" but never arrives, check the sending server's logs to see what happened in the onward delivery. Searching for the queue ID from queued as lets you follow just that message.

# Debian / Ubuntu (with rsyslog)
sudo grep A4D8E35FE9 /var/log/mail.log

# RHEL family (with rsyslog)
sudo grep A4D8E35FE9 /var/log/maillog

# Without rsyslog (systemd journal)
sudo journalctl --since "1 hour ago" | grep A4D8E35FE9

In the log, status=sent means the message was handed to the remote server, status=deferred means it is waiting for a retry after a temporary error, and status=bounced means it could not be delivered and an error message was returned. How to inspect messages waiting for a retry is covered in the article on the Postfix queue.

Caveats

  • Test addresses: send test messages to addresses you control. Sending many messages to other people's addresses or to non-existent domains can damage the sender's reputation
  • Handling credentials: do not put passwords directly on the command line; type them when prompted. Do not use AUTH over unencrypted connections (telnet, curl without STARTTLS)
  • Delivery is a separate check: 250 Ok: queued means "the server accepted it", not that it reached the recipient's inbox. Check on the receiving side as well, including the spam folder

Summary

  • Typing the SMTP dialog by hand with telnet or nc shows at which stage a message is rejected
  • curl sends a message file with --mail-from, --mail-rcpt and --upload-file, and handles STARTTLS and auth on port 587 with --ssl-reqd and --user
  • swaks sets TLS, auth, headers and attachments with arguments, and --quit-after RCPT checks a recipient without sending
  • 4xx reply codes are temporary errors and 5xx are permanent. After a message is accepted, follow its queue ID in the server logs

Related articles:

Questions about WordPress? The publisher of this site is based in Japan and replies in Japanese. Contact (Japanese)

お気軽にご相談ください

お見積りへ お問い合わせへ
▲