---
title: "SMTP Test: Send a Test Email and Inspect It"
description: "Send a test email through send.smtp.dev with swaks, curl, openssl, or your app's mailer, then read it back over IMAP, POP3, or the REST API."
canonical_url: "https://smtp.dev/docs/guides/send-test-email/"
---

> Index of every page on this site: [llms.txt](https://smtp.dev/llms.txt)

Send a message through the sandbox's SMTP server, then open what arrived: headers, MIME parts, raw source. `send.smtp.dev` only delivers to accounts in your sandbox, so a test email can't reach a real recipient.

## Settings

<table>
<thead>
  <tr>
    <th>
      Setting
    </th>
    
    <th>
      Value
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      Host
    </td>
    
    <td>
      <code>
        send.smtp.dev
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Port
    </td>
    
    <td>
      587, STARTTLS
    </td>
  </tr>
  
  <tr>
    <td>
      Username
    </td>
    
    <td>
      The account's full address
    </td>
  </tr>
  
  <tr>
    <td>
      Password
    </td>
    
    <td>
      The account's password
    </td>
  </tr>
  
  <tr>
    <td>
      Size
    </td>
    
    <td>
      Up to 20 MB per message
    </td>
  </tr>
</tbody>
</table>

Create two accounts on the [Accounts](https://smtp.dev/accounts/) page: one to send from, one to receive. The examples use `sender@test.example.com` and `qa@test.example.com`, and read the sender's password from `SMTP_PASSWORD`.

<note>

The recipient has to be an account in your sandbox. Any other address is refused.

</note>

## From the Command Line

### swaks

swaks is the usual SMTP test tool: `brew install swaks` or `apt install swaks`. It prints the whole SMTP conversation, so a refused login or recipient shows the server's exact reply.

```bash
swaks --server send.smtp.dev --port 587 --tls \
  --auth-user sender@test.example.com --auth-password "$SMTP_PASSWORD" \
  --from sender@test.example.com --to qa@test.example.com \
  --header "Subject: SMTP test" --body "Sent with swaks"
```

### curl

curl speaks SMTP, so there's nothing to install. Write the message to `message.txt`:

```text
From: sender@test.example.com
To: qa@test.example.com
Subject: SMTP test

Sent with curl.
```

Then send it:

```bash
curl smtp://send.smtp.dev:587 --ssl-reqd \
  --user "sender@test.example.com:$SMTP_PASSWORD" \
  --mail-from sender@test.example.com \
  --mail-rcpt qa@test.example.com \
  --upload-file message.txt
```

Add `-v` to see each command and reply.

### By Hand with openssl

Typing the SMTP commands yourself shows exactly where a conversation fails. Port 587 starts TLS before login, which telnet can't do, so connect with openssl:

```bash
openssl s_client -starttls smtp -connect send.smtp.dev:587 -crlf -quiet
```

`-crlf` sends the line endings SMTP expects. `-quiet` also stops openssl from reading a line that starts with `R` as a renegotiation command, which would otherwise swallow `RCPT TO`.

Build the login token in another terminal:

```bash
printf '\0%s\0%s' 'sender@test.example.com' "$SMTP_PASSWORD" | base64
```

Then type one line at a time and wait for each reply:

```text
EHLO localhost
AUTH PLAIN <token>
MAIL FROM:<sender@test.example.com>
RCPT TO:<qa@test.example.com>
DATA
From: sender@test.example.com
To: qa@test.example.com
Subject: SMTP test

Typed by hand.
.
QUIT
```

The `EHLO` reply lists what the server supports, including `SIZE`, its message size limit in bytes. The line with a single `.` ends the message.

<table>
<thead>
  <tr>
    <th>
      Reply
    </th>
    
    <th>
      Meaning
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        220
      </code>
    </td>
    
    <td>
      Ready
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        250
      </code>
    </td>
    
    <td>
      Accepted
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        235
      </code>
    </td>
    
    <td>
      Logged in
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        354
      </code>
    </td>
    
    <td>
      Send the message, end it with a line holding only <code>
        .
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        530
      </code>
    </td>
    
    <td>
      Log in first
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        535
      </code>
    </td>
    
    <td>
      Login failed
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        550
      </code>
    </td>
    
    <td>
      Refused, usually the recipient
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        552
      </code>
    </td>
    
    <td>
      Over the size limit
    </td>
  </tr>
</tbody>
</table>

## From Your App

Point the app's mailer at the sandbox. Sending from code:

<code-group>

```js [Node]
import nodemailer from 'nodemailer'

const transport = nodemailer.createTransport({
  host: 'send.smtp.dev',
  port: 587,
  requireTLS: true,
  auth: {
    user: 'sender@test.example.com',
    pass: process.env.SMTP_PASSWORD,
  },
})

await transport.sendMail({
  from: 'sender@test.example.com',
  to: 'qa@test.example.com',
  subject: 'SMTP test',
  text: 'Sent with Nodemailer',
})
```

```python [Python]
import os
import smtplib
from email.message import EmailMessage

msg = EmailMessage()
msg["From"] = "sender@test.example.com"
msg["To"] = "qa@test.example.com"
msg["Subject"] = "SMTP test"
msg.set_content("Sent with smtplib")

with smtplib.SMTP("send.smtp.dev", 587) as smtp:
    smtp.starttls()
    smtp.login("sender@test.example.com", os.environ["SMTP_PASSWORD"])
    smtp.send_message(msg)
```

```go [Golang]
package main

import (
    "net/smtp"
    "os"
)

func main() {
    from := "sender@test.example.com"
    msg := []byte("From: sender@test.example.com\r\n" +
        "To: qa@test.example.com\r\n" +
        "Subject: SMTP test\r\n" +
        "\r\n" +
        "Sent with net/smtp\r\n")

    auth := smtp.PlainAuth("", from, os.Getenv("SMTP_PASSWORD"), "send.smtp.dev")
    err := smtp.SendMail("send.smtp.dev:587", auth, from, []string{"qa@test.example.com"}, msg)
    if err != nil {
        panic(err)
    }
}
```

</code-group>

Or in your framework's mail settings:

<code-group>

```dotenv [Laravel]
MAIL_MAILER=smtp
MAIL_HOST=send.smtp.dev
MAIL_PORT=587
MAIL_USERNAME=sender@test.example.com
MAIL_PASSWORD=your-account-password
MAIL_FROM_ADDRESS=sender@test.example.com
```

```python [Django]
import os

EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = "send.smtp.dev"
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = "sender@test.example.com"
EMAIL_HOST_PASSWORD = os.environ["SMTP_PASSWORD"]
DEFAULT_FROM_EMAIL = "sender@test.example.com"
```

```ruby [Rails]
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
  address: "send.smtp.dev",
  port: 587,
  user_name: "sender@test.example.com",
  password: ENV["SMTP_PASSWORD"],
  authentication: :plain,
  enable_starttls: true
}
```

```properties [Spring Boot]
spring.mail.host=send.smtp.dev
spring.mail.port=587
spring.mail.username=sender@test.example.com
spring.mail.password=${SMTP_PASSWORD}
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true
```

</code-group>

Some frameworks can send a test email for you. Django has `python manage.py sendtestemail qa@test.example.com`. In Laravel, run `php artisan tinker`, then `Mail::raw('Sent with Laravel', fn ($message) => $message->to('qa@test.example.com')->subject('SMTP test'));`.

<warning>

Test runners often replace the mailer. Django's test runner switches to an in-memory backend, and Rails sets `delivery_method = :test` in the test environment. Mail sent from those tests never reaches SMTP, so set the SMTP settings on the server your end-to-end tests run against.

</warning>

## Read It Back

Open the recipient account in [Mail](https://smtp.dev/mail/accounts/), or log in to [webmail.smtp.dev](https://webmail.smtp.dev) with the account's address and password.

To script it, use the [REST API](https://smtp.dev/docs/api/). List the inbox, newest first, then fetch a message by `id`:

```bash
curl "https://api.smtp.dev/accounts/{accountId}/mailboxes/{mailboxId}/messages" \
  -H "X-API-KEY: $SMTP_DEV_API_KEY"

curl "https://api.smtp.dev/accounts/{accountId}/mailboxes/{mailboxId}/messages/{id}/source" \
  -H "X-API-KEY: $SMTP_DEV_API_KEY"
```

The `/source` endpoint returns the message exactly as it arrived. The [API quickstart](https://smtp.dev/docs/guides/email-testing-api/) covers finding the account and mailbox ids.

curl reads mail over IMAP and POP3 too, with the recipient's own credentials:

```bash
# UIDs of the messages in the inbox
curl "imaps://imap.smtp.dev/INBOX" --request "UID SEARCH ALL" \
  --user "qa@test.example.com:$QA_PASSWORD"

# one message, raw
curl "imaps://imap.smtp.dev/INBOX;UID=1" --user "qa@test.example.com:$QA_PASSWORD"

# the first message over POP3
curl "pop3s://pop.smtp.dev/1" --user "qa@test.example.com:$QA_PASSWORD"
```

## What to Check

- **Headers.** `From`, `Reply-To`, `Date`, and `Message-ID` are present and say what you intended.
- **Both parts.** A multipart message has a plain text and an HTML part. Read both. The plain text part is often the one nobody looked at.
- **Links.** They point at the right environment, with absolute URLs. A `localhost` link in a staging email is a common bug.
- **Attachments.** Filename, content type, and size match what the app attached. [Size limits](https://smtp.dev/docs/guides/email-size-limits/) covers how encoding grows them.
- **Encoding.** Non-ASCII names and subjects arrive intact.

## When It Fails

<table>
<thead>
  <tr>
    <th>
      Symptom
    </th>
    
    <th>
      Cause
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        535
      </code>
      
       after <code>
        AUTH
      </code>
    </td>
    
    <td>
      Wrong password, or the username isn't the full address
    </td>
  </tr>
  
  <tr>
    <td>
      Recipient refused
    </td>
    
    <td>
      The address isn't an account in your sandbox
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        552
      </code>
    </td>
    
    <td>
      The encoded message is over 20 MB
    </td>
  </tr>
  
  <tr>
    <td>
      Connection times out
    </td>
    
    <td>
      The network blocks outbound SMTP. Some networks block port 587, most cloud providers block 25
    </td>
  </tr>
  
  <tr>
    <td>
      TLS error right after connecting
    </td>
    
    <td>
      The client expects implicit TLS (port 465 settings). Use STARTTLS on 587
    </td>
  </tr>
</tbody>
</table>

## Testing Your Own Server

swaks, curl, and openssl work against any SMTP server: change the host, port, and credentials. The sandbox is useful as the far end. Send through your own server or provider to an address on a [domain whose MX points at mx.smtp.dev](https://smtp.dev/docs/setup/domain/), and the message arrives with [SPF, DKIM, and DMARC results](https://smtp.dev/docs/guides/spf-dkim-dmarc/) next to the raw source.

## Sitemap

See the full [sitemap](https://smtp.dev/sitemap.md) for all pages.
