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
AUTHover unencrypted connections (telnet,curlwithout STARTTLS) - Delivery is a separate check:
250 Ok: queuedmeans "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
telnetorncshows at which stage a message is rejected curlsends a message file with--mail-from,--mail-rcptand--upload-file, and handles STARTTLS and auth on port 587 with--ssl-reqdand--userswakssets TLS, auth, headers and attachments with arguments, and--quit-after RCPTchecks 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:
- How to check and manage the Postfix queue
- How to investigate mail problems with Postfix logs
- Sending WordPress mail over SMTP
Questions about WordPress? The publisher of this site is based in Japan and replies in Japanese. Contact (Japanese)