[GH-ISSUE #399] Content headers missing for inlined images #261

Closed
opened 2026-03-15 13:29:45 +03:00 by kerem · 10 comments
Owner

Originally created by @ndousson on GitHub (Dec 3, 2024).
Original GitHub issue: https://github.com/axllent/mailpit/issues/399

Hi, we recently moved from Maildev to Mailpit in order to keep the nicest tool on our local stack and for our review app environments.

After switching, everything was working nicely and as expected BUT one things was broken:

Inlined image rendering

After a quick analysis, we discovered that Content headers related to inlined images in the raw email was missing.

Here is a screenshot of the issue:

image

Here is the raw email from maildev and mailpit to compare:

maildev.txt
mailpit.txt

Let me know if you need more information.

Originally created by @ndousson on GitHub (Dec 3, 2024). Original GitHub issue: https://github.com/axllent/mailpit/issues/399 Hi, we recently moved from Maildev to Mailpit in order to keep the nicest tool on our local stack and for our review app environments. After switching, everything was working nicely and as expected BUT one things was broken: **Inlined image rendering** After a quick analysis, we discovered that Content headers related to inlined images in the raw email was missing. Here is a screenshot of the issue: ![image](https://github.com/user-attachments/assets/a5129ebe-36d6-41b6-8ce3-e0377c664a0a) Here is the raw email from maildev and mailpit to compare: [maildev.txt](https://github.com/user-attachments/files/17995574/maildev.txt) [mailpit.txt](https://github.com/user-attachments/files/17995575/mailpit.txt) Let me know if you need more information.
kerem closed this issue 2026-03-15 13:29:51 +03:00
Author
Owner

@axllent commented on GitHub (Dec 3, 2024):

Hi @ndousson. These are two very different emails - the mailpit.txt is missing all attachments in the raw email (ie: no attachments) whereas the maildev.txt is not. This means the inline images were not included by whatever generated that email before sending to the SMTP server (Mailpit). I don't think it has anything to do with Mailpit at all because if I send the contents of maildev.txt to my mailpit it works as expected (inline images show).

What are you using to generate these emails, and why would it be behaving different when sending to Mailpit?

<!-- gh-comment-id:2515342722 --> @axllent commented on GitHub (Dec 3, 2024): Hi @ndousson. These are two very different emails - the `mailpit.txt` is missing all attachments in the raw email (ie: no attachments) whereas the `maildev.txt` is not. This means the inline images were not included by whatever generated that email before sending to the SMTP server (Mailpit). I don't think it has anything to do with Mailpit at all because if I send the contents of `maildev.txt` to my mailpit it works as expected (inline images show). What are you using to generate these emails, and why would it be behaving different when sending to Mailpit?
Author
Owner

@ndousson commented on GitHub (Dec 4, 2024):

Hi @axllent,

After reading your answer, I double-checked how we were pushing messages to Maildev and confirmed that it was via SMTP.
Since we've implemented an ApiTransport, we're now sending messages via API to Mailpit.

I dug into the code with some debugging and found that the issue was at the AbstractTransport level, where the headers were missing. So you're absolutely right — this isn't related to Mailpit.

I apologize for the confusion and the noise. Thank you again for your response; it was incredibly helpful! 🙏🏻

<!-- gh-comment-id:2517637202 --> @ndousson commented on GitHub (Dec 4, 2024): Hi @axllent, After reading your answer, I double-checked how we were pushing messages to Maildev and confirmed that it was via SMTP. Since we've implemented an ApiTransport, we're now sending messages via API to Mailpit. I dug into the code with some debugging and found that the issue was at the AbstractTransport level, where the headers were missing. So you're absolutely right — this isn't related to Mailpit. I apologize for the confusion and the noise. Thank you again for your response; it was incredibly helpful! 🙏🏻
Author
Owner

@ndousson commented on GitHub (Dec 4, 2024):

Hi @axllent,

I'm back 🙈

After a deep dive with a colleague, we discovered that sending emails through the Mailpit API doesn't seem to support inline attachments when the attachment is base64-encoded.

Here’s the payload of a fake template we use in our tests, which should work but doesn’t:

fake_email.json

We also reviewed the Mailpit code to check if we missed something in the documentation about this behavior but didn’t find anything.

Do you agree that the API doesn’t (yet) support inline attachments with base64-encoded values?

If so, do you have an alternative approach that would allow us to continue using Mailpit? We can’t use SMTP as we rely on webhook functionality and the ID returned by the API call.

<!-- gh-comment-id:2517847250 --> @ndousson commented on GitHub (Dec 4, 2024): Hi @axllent, I'm back 🙈 After a deep dive with a colleague, we discovered that sending emails through the Mailpit API doesn't seem to support inline attachments when the attachment is base64-encoded. Here’s the payload of a fake template we use in our tests, which should work but doesn’t: [fake_email.json](https://github.com/user-attachments/files/18010787/fake_email.json) We also reviewed the Mailpit code to check if we missed something in the documentation about this behavior but didn’t find anything. Do you agree that the API doesn’t (yet) support inline attachments with base64-encoded values? If so, do you have an alternative approach that would allow us to continue using Mailpit? We can’t use SMTP as we rely on webhook functionality and the ID returned by the API call.
Author
Owner

@ndousson commented on GitHub (Dec 4, 2024):

You currently use AddAttachment from:

https://github.com/jhillyerd/enmime/blob/main/builder.go#L223.

You should add the support of AddInline from:

https://github.com/jhillyerd/enmime/blob/main/builder.go#L262

WDYT ?

<!-- gh-comment-id:2517894648 --> @ndousson commented on GitHub (Dec 4, 2024): You currently use `AddAttachment` from: https://github.com/jhillyerd/enmime/blob/main/builder.go#L223. You should add the support of `AddInline` from: https://github.com/jhillyerd/enmime/blob/main/builder.go#L262 WDYT ?
Author
Owner

@axllent commented on GitHub (Dec 5, 2024):

@ndousson Thanks for the investigation. Firstly there are a few things wrong with your JSON file (for use with Mailpit), and I'm not sure where you are getting your JSON structure from. Have you seen the docs for sending via the Mailpit HTTP API?

  1. You need to use Attachments, not inlinedAttachments
  2. Attachments have a Content field, not base64Content
  3. Mailpit does not (currently) support specifying contentType for attachments, they are automatically detected based on the binary data extracted from the base64-encoded Content
  4. Your attachment filenames in your JSON all lack any file extension, so "filename": "greyLogo" should be "filename": "greyLogo.png". It's not that it won't work, it is just bad practice not to use file extensions because your mileage will vary with email program support.

Based on your feedback, I like the idea of being able to optionally set the Content-Id to determine if an attachment is inline or not, and makes this backwards compatible with existing implementations. As you noted, Mailpit currently only supports regular attachments. That being said, many email programs do support "inlining" attachments simply by using their filename, so <img src="test.gif" /> tends to work if the email has a test.gif attachment. I'm definitely not saying this is best practice though!

I am now testing the option to optionally set both the Content-Type and Content-Id via the API.

  1. If ContentType is left empty (or not set) then it is auto-detected
  2. If ContentID is set, then the attachment is inline, otherwise just a regular attachment

Does this make sense to you, and do you agree that this would provide you with the necessary functionality to test your application?

<!-- gh-comment-id:2519056193 --> @axllent commented on GitHub (Dec 5, 2024): @ndousson Thanks for the investigation. Firstly there are a few things wrong with your JSON file (for use with Mailpit), and I'm not sure where you are getting your JSON structure from. Have you seen [the docs](https://mailpit.axllent.org/docs/api-v1/view.html#post-/api/v1/send) for sending via the Mailpit HTTP API? 1. You need to use `Attachments`, not `inlinedAttachments` 2. Attachments have a `Content` field, not `base64Content` 3. Mailpit does not (currently) support specifying `contentType` for attachments, they are automatically detected based on the binary data extracted from the base64-encoded `Content` 4. Your attachment filenames in your JSON all lack any file extension, so `"filename": "greyLogo"` should be `"filename": "greyLogo.png"`. It's not that it won't work, it is just bad practice not to use file extensions because your mileage will vary with email program support. Based on your feedback, I like the idea of being able to optionally set the Content-Id to determine if an attachment is inline or not, and makes this backwards compatible with existing implementations. As you noted, Mailpit currently only supports regular attachments. That being said, many email programs do support "inlining" attachments simply by using their filename, so `<img src="test.gif" />` tends to work if the email has a `test.gif` attachment. I'm definitely not saying this is best practice though! I am now testing the option to optionally set both the Content-Type and Content-Id via the API. 1. If `ContentType` is left empty (or not set) then it is auto-detected 2. If `ContentID` is set, then the attachment is inline, otherwise just a regular attachment Does this make sense to you, and do you agree that this would provide you with the necessary functionality to test your application?
Author
Owner

@ndousson commented on GitHub (Dec 5, 2024):

Thank you, @axllent, for your feedback.

You're absolutely right about the implementation. While I wasn't the one who originally implemented the payload, I agree that it doesn't align with the Mailpit API contract.

I just updated it a few minutes ago to match the expected behavior. 👍🏻

I also appreciate your suggestion regarding the Content-Type and Content-ID.

If the final result aligns with the example below, it should work perfectly.

Current data part on Mailpit with current API contract:

Content-Type: image/gif; name=avatar.gif
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename=avatar.gif

<based_64_encoded>

Expected (example from Maildev):

Content-ID: <cd19d7feae616eca67de89a4dc5229b9@symfony>
Content-Type: image/gif; name="cd19d7feae616eca67de89a4dc5229b9@symfony"
Content-Transfer-Encoding: base64
Content-Disposition: inline; name="cd19d7feae616eca67de89a4dc5229b9@symfony"; filename=avatar

<based_64_encoded>

If you have a pre-release, I'm ready to test it 👍🏻

FYI, the payload that was used is the same than the one for Mailjet.

<!-- gh-comment-id:2519539131 --> @ndousson commented on GitHub (Dec 5, 2024): Thank you, @axllent, for your feedback. You're absolutely right about the implementation. While I wasn't the one who originally implemented the payload, I agree that it doesn't align with the Mailpit API contract. I just updated it a few minutes ago to match the expected behavior. 👍🏻 I also appreciate your suggestion regarding the Content-Type and Content-ID. If the final result aligns with the example below, it should work perfectly. ✨ Current data part on Mailpit with current API contract: ``` Content-Type: image/gif; name=avatar.gif Content-Transfer-Encoding: base64 Content-Disposition: attachment; filename=avatar.gif <based_64_encoded> ``` Expected (example from Maildev): ``` Content-ID: <cd19d7feae616eca67de89a4dc5229b9@symfony> Content-Type: image/gif; name="cd19d7feae616eca67de89a4dc5229b9@symfony" Content-Transfer-Encoding: base64 Content-Disposition: inline; name="cd19d7feae616eca67de89a4dc5229b9@symfony"; filename=avatar <based_64_encoded> ``` If you have a pre-release, I'm ready to test it 👍🏻 _FYI, the payload that was used is the same than the one for Mailjet._
Author
Owner

@axllent commented on GitHub (Dec 5, 2024):

Excellent! Assuming you are running Mailpit's Docker environment you can use the axllent/mailpit:edge build which has just been updated with this change. I've also taken the liberty to modify your original payload: fake_email.json. I have added the correct casing to the JSON keys, although this isn't required as it will accept any casing (eg: attachments is the same as AttaCHMenTs) :)

As for the payload format vs: Mailjet - I originally did a comparison between several popular HTTP API services and every single one did it differently to each other. In the end I went with what felt was a good combination.

Content-Disposition: inline; filename=icon-love.gif
Content-Id: <icon-love>
Content-Transfer-Encoding: base64
Content-Type: image/gif; name=icon-love.gif

Please let me know if this resolves your issue?

<!-- gh-comment-id:2519632411 --> @axllent commented on GitHub (Dec 5, 2024): Excellent! Assuming you are running Mailpit's Docker environment you can use the `axllent/mailpit:edge` build which has just been updated with this change. I've also taken the liberty to modify your original payload: [fake_email.json](https://github.com/user-attachments/files/18019887/fake_email.json). I have added the correct casing to the JSON keys, although this isn't required as it will accept any casing (eg: `attachments` is the same as `AttaCHMenTs`) :) As for the payload format vs: Mailjet - I originally did a comparison between several popular HTTP API services and every single one did it differently to each other. In the end I went with what felt was a good combination. ``` Content-Disposition: inline; filename=icon-love.gif Content-Id: <icon-love> Content-Transfer-Encoding: base64 Content-Type: image/gif; name=icon-love.gif ``` Please let me know if this resolves your issue?
Author
Owner

@ndousson commented on GitHub (Dec 5, 2024):

I just tested the edge release of the Docker image, and it works perfectly!

Everything is now resolved on our side. Huge thanks for your responsiveness and help with this issue. 🙏🏻

We'll wait for the next tagged release to upgrade. In the meantime, I'll use the edge release in our non-production environment to unblock our developers with the images.

<!-- gh-comment-id:2519651532 --> @ndousson commented on GitHub (Dec 5, 2024): I just tested the edge release of the Docker image, and it works perfectly! ✨ Everything is now resolved on our side. Huge thanks for your responsiveness and help with this issue. 🙏🏻 We'll wait for the next tagged release to upgrade. In the meantime, I'll use the edge release in our non-production environment to unblock our developers with the images.
Author
Owner

@axllent commented on GitHub (Dec 5, 2024):

Awesome, thanks for the quick feedback. I will be releasing a new version within the next 2-3 days (by the end of the weekend). I'll let you know on this issue when I do 👍

<!-- gh-comment-id:2519664365 --> @axllent commented on GitHub (Dec 5, 2024): Awesome, thanks for the quick feedback. I will be releasing a new version within the next 2-3 days (by the end of the weekend). I'll let you know on this issue when I do :+1:
Author
Owner

@axllent commented on GitHub (Dec 8, 2024):

This feature has now been officially released in v1.21.6. Enjoy!

<!-- gh-comment-id:2525351560 --> @axllent commented on GitHub (Dec 8, 2024): This feature has now been officially released in [v1.21.6](https://github.com/axllent/mailpit/releases/tag/v1.21.6). Enjoy!
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#261
No description provided.