HTML mail needs MIME-Version: 1.0 and a Content-Type. HTML alone scores worse with spam filters, so send multipart/alternative: each part starts with -- plus a boundary, --boundary-- ends the body, and clients render the last part they understand, so text goes first.
<?php
declare(strict_types=1);
$b = 'b-' . bin2hex(random_bytes(8)); // boundary: must never occur in the body
$body = "--$b\r\nContent-Type: text/plain; charset=UTF-8\r\n\r\nOrder #1001 confirmed.\r\n"
. "--$b\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n<h1>Order confirmed</h1>\r\n"
. "--$b--\r\n";
var_dump(mail('reader@example.com', mb_encode_mimeheader('Order #1001 – confirmed'), $body, [
'From' => 'orders@example.com', 'MIME-Version' => '1.0',
'Content-Type' => "multipart/alternative; boundary=\"$b\""]));Output
$ php -d sendmail_path='docker exec -i mailpit /mailpit sendmail' mail-alt.php bool(true) $ curl -s localhost:8019/api/v1/message/latest/headers | jq -r '.Subject[0]' Order #1001 =?UTF-8?B?4oCTIGNvbmZpcm1lZA==?=
Headers are ASCII only, so mb_encode_mimeheader() made the en dash an RFC 2047 encoded word.