[GH-ISSUE #546] Search API returns total count but empty messages array when searching for custom header values #354

Closed
opened 2026-03-15 14:03:20 +03:00 by kerem · 3 comments
Owner

Originally created by @mstroeve on GitHub (Jul 30, 2025).
Original GitHub issue: https://github.com/axllent/mailpit/issues/546

Description

When migrating from Mailhog to Mailpit, we noticed that the search API finds messages (returns correct total count) but returns an empty messages array when searching for specific values in custom headers or message content.

Steps to Reproduce

  1. Send an email with a custom header, for example:

    X-mailhog-id: 74f4096e936eec08dc05a9b98d7df306
    
  2. Verify the email is received and the header is present:

    curl -s "http://localhost:8025/api/v1/message/{ID}/raw" | grep "X-mailhog-id"
    # Output: X-mailhog-id: 74f4096e936eec08dc05a9b98d7df306
    
  3. Search for this header value using the search API:

    curl -s "http://localhost:8025/api/v1/search?query=74f4096e936eec08dc05a9b98d7df306"
    

Expected Behavior

The search API should return the messages array containing the email(s) with the matching header value.

Actual Behavior

The API returns:

{
  "total": 3,
  "unread": 0,
  "count": 0,
  "messages_count": 0,
  "messages": []
}

Note that total is 3 (correct - there are 3 emails with this header), but messages is empty.

Environment

  • Mailpit version: Latest (axllent/mailpit Docker image)
  • OS: macOS / Docker
  • Installation method: Docker

Use Case

We're using custom headers to filter emails by tenant/site in a multi-tenant application for staging. In Mailhog, we could search for these custom header values to show only emails belonging to a specific tenant. This is critical for our development environment where multiple sites share the same Mailpit instance.

Workaround

Currently, we have to:

  1. Fetch all messages (/api/v1/messages)
  2. Iterate through each message
  3. Fetch the raw content (/api/v1/message/{ID}/raw)
  4. Check if it contains our custom header

This is inefficient and doesn't scale well.

Additional Context

  • The same issue occurs when searching for any content that exists in the messages
  • Other search queries like is:unread work correctly
  • Tag-based searches also seem affected (e.g., searching for specific tags returns total count but no messages)

Is this a known limitation or a bug? Any guidance would be appreciated.

Originally created by @mstroeve on GitHub (Jul 30, 2025). Original GitHub issue: https://github.com/axllent/mailpit/issues/546 ## Description When migrating from Mailhog to Mailpit, we noticed that the search API finds messages (returns correct `total` count) but returns an empty `messages` array when searching for specific values in custom headers or message content. ## Steps to Reproduce 1. Send an email with a custom header, for example: ``` X-mailhog-id: 74f4096e936eec08dc05a9b98d7df306 ``` 2. Verify the email is received and the header is present: ```bash curl -s "http://localhost:8025/api/v1/message/{ID}/raw" | grep "X-mailhog-id" # Output: X-mailhog-id: 74f4096e936eec08dc05a9b98d7df306 ``` 3. Search for this header value using the search API: ```bash curl -s "http://localhost:8025/api/v1/search?query=74f4096e936eec08dc05a9b98d7df306" ``` ## Expected Behavior The search API should return the messages array containing the email(s) with the matching header value. ## Actual Behavior The API returns: ```json { "total": 3, "unread": 0, "count": 0, "messages_count": 0, "messages": [] } ``` Note that `total` is 3 (correct - there are 3 emails with this header), but `messages` is empty. ## Environment - Mailpit version: Latest (axllent/mailpit Docker image) - OS: macOS / Docker - Installation method: Docker ## Use Case We're using custom headers to filter emails by tenant/site in a multi-tenant application for staging. In Mailhog, we could search for these custom header values to show only emails belonging to a specific tenant. This is critical for our development environment where multiple sites share the same Mailpit instance. ## Workaround Currently, we have to: 1. Fetch all messages (`/api/v1/messages`) 2. Iterate through each message 3. Fetch the raw content (`/api/v1/message/{ID}/raw`) 4. Check if it contains our custom header This is inefficient and doesn't scale well. ## Additional Context - The same issue occurs when searching for any content that exists in the messages - Other search queries like `is:unread` work correctly - Tag-based searches also seem affected (e.g., searching for specific tags returns total count but no messages) Is this a known limitation or a bug? Any guidance would be appreciated.
kerem 2026-03-15 14:03:20 +03:00
  • closed this issue
  • added the
    stale
    label
Author
Owner

@axllent commented on GitHub (Jul 31, 2025):

Hi @mstroeve, thanks for the detailed issue. I'll do my best to answer the various points without dragging it out too much.

The first (and most important) thing to note is that Mailpit's search does not search the entire raw email - it contains an index of some headers (From, To, Cc, Bcc, Subject and Message-ID), and a plain text representation of the HTML content (if it exists) or alternatively the content of the plain/text part if no HTML part exists. It also includes the names (but not contents) of attachments.

The reason it only includes these fields is quite simple: Mailpit uses a sqlite database to store and search messages ,which is fast, lightweight (CPU and RAM), and fairly flexible (not limited to just exact phrases). MailHog, on the other hand, will literally open every message and attempt to match the search phrase against anything in the entire email - which is very inefficient/slow, CPU & memory intensive, and not flexible in terms of searching. So the bottom line is they work very differently.

In your case you're wanting to search the value of a particular custom header which is not included in the search index. What you are trying to do cannot be achieved in this manner.

Note that total is 3 (correct - there are 3 emails with this header), but messages is empty.

Actually the total is the total number of messages in the database, not the number of messages in the search results (that is messages_count as per the schema in the API docs ~ you're mixing the two). Mailpit is returning that there are 0 results in your search, but that there are 3 messages in total in the database (within this tenant).

Alternative approach

Given that you cannot search for custom headers, why not try it a different way. You could either:

  1. Set the Message-ID rather than a custom X-MailHog-ID - then you can search for http://localhost:8025/api/v1/search?query=message-id:<your-id>
  2. Set a X-Tags: <your-id> header and then filter by that tag http://localhost:8025/api/v1/search?query=tag:<your-id>

Both of these options would get you where you need to be. Hope this helps?

<!-- gh-comment-id:3138535131 --> @axllent commented on GitHub (Jul 31, 2025): Hi @mstroeve, thanks for the detailed issue. I'll do my best to answer the various points without dragging it out too much. The first (and most important) thing to note is that Mailpit's search does **not** search the entire raw email - it contains an index of _some_ headers (From, To, Cc, Bcc, Subject and Message-ID), and a plain text representation of the HTML content (if it exists) or alternatively the content of the plain/text part if no HTML part exists. It also includes the names (but not contents) of attachments. The reason it only includes these fields is quite simple: Mailpit uses a sqlite database to store and search messages ,which is fast, lightweight (CPU and RAM), and fairly [flexible](https://mailpit.axllent.org/docs/usage/search-filters/) (not limited to just exact phrases). MailHog, on the other hand, will literally open every message and attempt to match the search phrase against anything in the entire email - which is very inefficient/slow, CPU & memory intensive, and not flexible in terms of searching. So the bottom line is they work very differently. In your case you're wanting to search the value of a particular custom header which is not included in the search index. What you are trying to do cannot be achieved in this manner. > Note that `total` is 3 (correct - there are 3 emails with this header), but messages is empty. Actually the `total` is the total number of messages in the database, not the number of messages in the search results (that is `messages_count` as per the schema in the [API docs](https://mailpit.axllent.org/docs/api-v1/view.html#delete-/api/v1/search) ~ you're mixing the two). Mailpit is returning that there are `0` results in your search, but that there are 3 messages in total in the database (within this tenant). ## Alternative approach Given that you cannot search for custom headers, why not try it a different way. You could either: 1. Set the `Message-ID` rather than a custom `X-MailHog-ID` - then you can search for `http://localhost:8025/api/v1/search?query=message-id:<your-id>` 2. Set a `X-Tags: <your-id>` header and then filter by that tag `http://localhost:8025/api/v1/search?query=tag:<your-id>` Both of these options would get you where you need to be. Hope this helps?
Author
Owner

@github-actions[bot] commented on GitHub (Aug 8, 2025):

This issue has been marked as stale because it has been open for 7 days with no activity.

<!-- gh-comment-id:3166349148 --> @github-actions[bot] commented on GitHub (Aug 8, 2025): This issue has been marked as stale because it has been open for 7 days with no activity.
Author
Owner

@github-actions[bot] commented on GitHub (Aug 11, 2025):

This issue was closed because there has been no activity since being marked as stale.

<!-- gh-comment-id:3173098517 --> @github-actions[bot] commented on GitHub (Aug 11, 2025): This issue was closed because there has been no activity since being marked as stale.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/mailpit#354
No description provided.