2026年11月13日
2026年11月13日
Fix Garbled HTML Emails from wp_mail() in WordPress: Causes and Fixes
This article is a localized edition of the Japanese original. Read the Japanese original
When an HTML email sent with wp_mail() arrives garbled, the cause is usually one of three things: "the Content-Type and character encoding (charset) settings," "the transfer encoding of the body," or "HTML entities left in the subject." If you look at the source (headers) of the received email, you can narrow down which one it is fairly well. This article explains how to read the symptoms, then how to check and fix each cause, starting with the most likely.
The code assumes you put it in a child theme's functions.php or in your own plugin or mu-plugin. Changing the email sending settings can affect every email on the site, such as contact form and member registration notifications. Take a backup before making changes, and check them on a staging environment or with a test recipient address before applying them to production.
First, tell the symptoms apart
"Garbled text" covers several things, but how it looks gives you a hint about the cause.
- Tags such as
<p>and<br>are displayed as they are in the body: this is not garbled text. The email is being sent as plain text. The Content-Type setting is missing. - The Japanese text in the body turns into a string of symbols such as
縺ゅ・orã‚: the sender and the receiver interpret the character encoding differently. Suspect the charset setting or the file's save format. - Only the middle of a long body breaks, or a
!or space appears in the middle of a tag: a line may have been too long and was wrapped somewhere along the route. This is a transfer encoding problem. - The subject shows
–or&as they are: this is not garbled text. HTML entities from a post title or similar are left in the subject. - Only the subject is shown as
=?UTF-8?B?...: you may have encoded the subject yourself, so it is encoded twice.
Check the source of the received email
To find the cause, use your mail client's "View source" or "Show original" feature (webmail such as Gmail has it too; the name differs by service and version). These are the three lines to look at.
Subject: =?UTF-8?B?44GK5ZWP44GE5ZCI44KP44Gb44KS5Y+X44GR5LuY44GR44G+44GX44Gf?=
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: 8bit
If Content-Type is text/plain, the email was not sent as HTML. If charset is anything other than UTF-8 (such as iso-8859-1), there is a problem with the character encoding setting. Content-Transfer-Encoding is how the body is carried: 8bit sends the body as it is, while base64 and quoted-printable convert it to 7-bit characters only before carrying it.
During development, a tool that catches email instead of sending it to the real recipient and shows its contents (such as Mailpit) lets you check the headers and body as many times as you like when you run it in a local environment.
Cause 1: Content-Type is not text/html
Unless you specify otherwise, wp_mail() sends email as text/plain. To send HTML, pass the Content-Type in the headers. You can specify the charset there too.
<?php
function mytheme_send_html_mail( $to, $subject, $html ) {
$headers = array( 'Content-Type: text/html; charset=UTF-8' );
return wp_mail( $to, $subject, $html, $headers );
}
Returning text/html from the wp_mail_content_type filter is also a commonly shown method, but if you leave the filter attached, even the plain-text emails sent by WordPress core and other plugins, such as password resets, are treated as HTML, and line breaks disappear or links break. If you use the filter, add it right before sending and remove it right afterward with remove_filter(). Specifying it in the headers for each send, as above, limits the scope of the effect better.
Cause 2: The charset setting does not match the actual character encoding
If no charset is specified in the headers, WordPress uses the site's character encoding (the option blog_charset, normally UTF-8). Check the following points in order.
The site's character encoding: If WP-CLI is available, you can check it with wp option get blog_charset. After migrating an old site, for example, it can be something other than UTF-8. Changing the value also affects how existing content is displayed, so if you change it, take a database backup and verify on staging first.
The save format of the email template file: If you keep the HTML template in a separate file and that file is saved as Shift_JIS or similar, the content will not match even if you declare the charset as UTF-8, and it will be garbled. In your editor, save the file again with the character encoding set to UTF-8. For PHP files, use UTF-8 without BOM. With a BOM, extra characters can also appear at the start of the output.
Plugins that use the wp_mail_charset filter: If a plugin or code that changes the email character encoding to ISO-2022-JP or similar is installed, it can cause garbled text when combined with an HTML body. Some sites still have such code left over from measures for old mobile phones, so search for code that uses wp_mail_charset or phpmailer_init and check it. Today's major email clients handle UTF-8, so unless there is a special reason, it is safest to standardize on UTF-8.
The meta tag in the HTML: Email clients mainly look at the charset in the email headers, but including <meta charset="UTF-8"> in the <head> of the HTML body can reduce mismatches in some environments.
Cause 3: A line in the body is too long and gets wrapped partway
The email standard sets an upper limit on line length, and if you minify the HTML into a single line with no line breaks, it exceeds that limit. If a mail server along the route inserts a line break on its own, the text can be cut in the middle of a Japanese character and garbled, or extra characters can end up in the middle of a tag. PHPMailer, which WordPress uses, has logic that switches the transfer encoding when it detects a line that is too long, but that assumption can change when you go through an SMTP plugin or an external sending service.
There are two countermeasures. One is to keep line breaks in the HTML template at reasonable intervals. The other is to set the transfer encoding to base64 only for HTML emails. With the phpmailer_init action you can adjust the PHPMailer object just before sending.
<?php
add_action( 'phpmailer_init', function ( $phpmailer ) {
if ( 'text/html' === $phpmailer->ContentType ) {
$phpmailer->CharSet = 'UTF-8';
$phpmailer->Encoding = 'base64';
} else {
// The same object is used for the next send too, so reset text emails to the default
$phpmailer->Encoding = '8bit';
}
} );
wp_mail() reuses the same PHPMailer object within a single request. If you change the settings only for HTML and do not restore them, they remain in effect for the plain-text emails that follow, so the example above handles both cases. Using base64 makes the body a little larger, but it is less affected by wrapping along the route.
Cause 4: HTML entities are left in the subject
A post title obtained with get_the_title() may have symbols converted into character references such as – or ’ for display. This is not a problem in a body that is displayed as HTML, but the subject is treated as text, so they show up as they are. Decode them before using the title in the subject.
<?php
$post_title = wp_specialchars_decode( get_the_title( $post_id ), ENT_QUOTES );
$subject = sprintf( '[New] %s', $post_title );
For the same reason, if you put the site name in the subject, decode it first as well, like wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES ). WordPress core's notification emails also handle the site name this way.
Cause 5: You are encoding the subject or body yourself
If you look up how to send Japanese email, you will find examples that encode the subject with mb_encode_mimeheader() and examples that convert the body with mb_convert_encoding(). However, with wp_mail() the internal PHPMailer encodes the subject, so encoding it yourself as well makes it double-encoded, and the subject is displayed as a string starting with =?UTF-8?B?. Pass UTF-8 strings to wp_mail() as they are.
Cause 6: Settings in an SMTP plugin or sending service
If you send through an SMTP plugin such as WP Mail SMTP or FluentSMTP, or through the API of an external sending service, the actual sending may be done by the plugin's own processing. In that case, the phpmailer_init adjustment above may have no effect (it depends on the sending method). If you temporarily deactivate the SMTP plugin and the garbled text goes away, you have found that the cause is on the plugin side. Check each plugin's documentation to see whether its settings have an item related to character encoding or transfer encoding (as of October 2026).
Verifying and logging send failures
After the fix, receive the email and check it in the typical environments your readers use, such as Gmail, Outlook and smartphone mail apps. The same HTML can display differently depending on the email client.
You can log whether the sending itself failed with the wp_mail_failed action.
<?php
add_action( 'wp_mail_failed', function ( $error ) {
if ( defined( 'WP_DEBUG_LOG' ) && WP_DEBUG_LOG ) {
error_log( 'wp_mail failed: ' . $error->get_error_message() );
}
} );
Do not write personal information such as the recipient or the body to the log; limit it to the error message. When your investigation is finished, restore the debug log settings as well.
Summary
- If you can see the tags as they are, the email is being sent as plain text. Specify
Content-Type: text/html; charset=UTF-8in the headers for each send. - If Japanese turns into a string of symbols, check
blog_charset, the save format of the template file and any code that useswp_mail_charset, in that order, and standardize on UTF-8. - If only the middle of a long line breaks, add line breaks to the HTML or set the transfer encoding of HTML emails to
base64withphpmailer_init. - Decode things like
–in the subject withwp_specialchars_decode(), and do not encode the subject yourself. - Checking the headers in the source of the received email lets you narrow down the cause quickly.
- How to customize wp_mail() in WordPress
- Causes of garbled email and the basics of character encoding
Questions about WordPress? The publisher of this site is based in Japan and replies in Japanese. Contact (Japanese)