[wp-trac] [WordPress Trac] #15448: wp_mail() sets Content-Type header twice for multipart emails
WordPress Trac
noreply at wordpress.org
Sun Aug 17 14:30:21 UTC 2025
#15448: wp_mail() sets Content-Type header twice for multipart emails
-------------------------------------------------+-------------------------
Reporter: rmccue | Owner: SirLouen
Type: defect (bug) | Status: accepted
Priority: high | Milestone: 6.9
Component: Mail | Version: 2.8
Severity: normal | Resolution:
Keywords: has-patch has-unit-tests has-test- | Focuses:
info needs-testing |
-------------------------------------------------+-------------------------
Changes (by SirLouen):
* status: reviewing => accepted
* priority: normal => high
* owner: SergeyBiryukov => SirLouen
* version: => 2.8
* milestone: Future Release => 6.9
* keywords: has-patch has-unit-tests has-test-info reported-upstream =>
has-patch has-unit-tests has-test-info needs-testing
* type: feature request => defect (bug)
Comment:
After reviewing the PHPMailer library for a good while, I've noticed that
it was never a problem of this library as the members from StackOverflow
suggested. I was able to create a full example in PHPMailer and I wasn't
able to reproduce it there, so I came to the WP code and found the
problem.
Currently, the construction of the `Content-Type` is currently being done
here:
https://github.com/WordPress/wordpress-
develop/blob/dd2d7efd9dc11a362056be6cd7fc75ccd6b0cf22/src/wp-
includes/pluggable.php#L517-L519
The issue is that this conditional only happens if there are `$headers`.
Generally there are other headers, which is not strictly necessary to send
a multipart.
@rmccue was very tricky because he provided a superb example.
https://core.trac.wordpress.org/ticket/15448#comment:70
Including an extra random header to trigger this, but still his code was
not working.
Why?
Because it was incorrectly formatted.
[https://www.w3.org/Protocols/rfc1341/7_2_Multipart.html According to the
RFC1341], there should be an empty line between each of the Content-Types
inside the body.
In the @rmccue example:
{{{
$body = '--TestBoundary
Content-Type: text/plain; charset=UTF-8
This is a test email body.
--TestBoundary
Content-Type: text/html; charset=UTF-8
<html><body>This is a test email body.</body></html>
--TestBoundary--
';
$headers = [
'Example-Custom: value',
'Content-Type: multipart/alternative; boundary="TestBoundary"',
];
wp_mail( 'test at example.com', 'Test', $body, $headers );
}}}
There is no space in between the `Content-Type` for the `text/html` and
the HTML content. This is why, despite of providing this little extra
header to trigger the conditional, it is not working.
The question is, why handle the `Content-Type` so late and in the Headers
section if the `Content-Type` is [https://github.com/WordPress/wordpress-
develop/blob/dd2d7efd9dc11a362056be6cd7fc75ccd6b0cf22/src/wp-
includes/pluggable.php#L320-L335 being handled much earlier] and
specifically in the `Content-Type` part that will be straightly passed to
the `$phpmailer->Content-Type` instance? It happens to be a bug introduced
in 2.8, more specifically in [11136], wrongly added with the requirement
of extra headers.
My solution is simple: removing the late `Content-Type` handling for
multipart and bringing it back to the specific `content-type` section.
Furthermore, I provide a little regex so we can manage every single type
of multipart available (not only `alternative`, but also `mixed`,
`related`, etc…).
I've added an additional unit test to check for this. Despite this having
now been completely reviewed and ready to be shipped, I would like to add
a pair of manual tests to fully close this ticket.
== Testing Information
To test this, I would recommend doing the two following tests:
1. Create a little plugin that sends a `multipart/alternative` email, and
check the results.
2. Same but creating a `multipart/mixed`. This will also prove that this
won't cause a regression with [11136].
--
Ticket URL: <https://core.trac.wordpress.org/ticket/15448#comment:86>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform
More information about the wp-trac
mailing list