Email Hooks
Actions and filters for customizing WordPress email functionality.
Source: wp-includes/pluggable.php, wp-includes/ms-functions.php
Filters
wp_mail
Filter all wp_mail() arguments before processing.
apply_filters( 'wp_mail', array $args )
Since: 2.2.0
| Parameter | Type | Description |
|---|---|---|
$args |
array | Associative array of wp_mail arguments |
$args keys:
to(string|array) – Email addressessubject(string) – Email subjectmessage(string) – Message bodyheaders(string|array) – Additional headersattachments(string|array) – File paths to attachembeds(string|array) – File paths to embed inline (6.9.0+)
add_filter( 'wp_mail', function( $args ) {
// Add BCC to all emails
$args['headers'][] = 'Bcc: [email protected]';
return $args;
});
pre_wp_mail
Short-circuit wp_mail() before any processing.
apply_filters( 'pre_wp_mail', null|bool $return, array $atts )
Since: 5.7.0
| Parameter | Type | Description |
|---|---|---|
$return |
null|bool | Return non-null to skip sending |
$atts |
array | The wp_mail arguments |
add_filter( 'pre_wp_mail', function( $return, $atts ) {
// Block emails to specific domain
if ( str_contains( $atts['to'], '@blocked.com' ) ) {
return false;
}
return $return;
}, 10, 2 );
wp_mail_from
Filter the "From" email address.
apply_filters( 'wp_mail_from', string $from_email )
Since: 2.2.0
| Parameter | Type | Description |
|---|---|---|
$from_email |
string | The from email address |
Default: wordpress@{site_domain}
add_filter( 'wp_mail_from', function( $from ) {
return '[email protected]';
});
wp_mail_from_name
Filter the "From" name.
apply_filters( 'wp_mail_from_name', string $from_name )
Since: 2.3.0
| Parameter | Type | Description |
|---|---|---|
$from_name |
string | The from name |
Default: WordPress
add_filter( 'wp_mail_from_name', function( $name ) {
return get_bloginfo( 'name' );
});
wp_mail_content_type
Filter the email content type.
apply_filters( 'wp_mail_content_type', string $content_type )
Since: 2.3.0
| Parameter | Type | Description |
|---|---|---|
$content_type |
string | The content type |
Default: text/plain (or from Content-Type header)
add_filter( 'wp_mail_content_type', function( $type ) {
return 'text/html';
});
Warning: Setting globally to text/html affects all emails. Consider using per-email headers instead.
wp_mail_charset
Filter the email character set.
apply_filters( 'wp_mail_charset', string $charset )
Since: 2.3.0
| Parameter | Type | Description |
|---|---|---|
$charset |
string | The character set |
Default: Value from get_bloginfo('charset') or Content-Type header
add_filter( 'wp_mail_charset', function( $charset ) {
return 'UTF-8';
});
wp_mail_embed_args
Filter arguments for embedded images.
apply_filters( 'wp_mail_embed_args', array $args )
Since: 6.9.0
| Parameter | Type | Description |
|---|---|---|
$args |
array | Arguments for PHPMailer’s addEmbeddedImage() |
$args keys:
path(string) – Path to the filecid(string) – Content-ID for the imagename(string) – Filename (defaults to basename)encoding(string) – Encoding type (default:base64)type(string) – MIME type (empty = auto-detect)disposition(string) – Disposition (default:inline)
Actions
phpmailer_init
Fires after PHPMailer is initialized, before sending. Primary hook for SMTP configuration.
do_action_ref_array( 'phpmailer_init', array( PHPMailer &$phpmailer ) )
Since: 2.2.0
| Parameter | Type | Description |
|---|---|---|
$phpmailer |
PHPMailer | PHPMailer instance (passed by reference) |
SMTP Configuration Example:
add_action( 'phpmailer_init', function( $phpmailer ) {
$phpmailer->isSMTP();
$phpmailer->Host = 'smtp.example.com';
$phpmailer->SMTPAuth = true;
$phpmailer->Port = 587;
$phpmailer->Username = '[email protected]';
$phpmailer->Password = 'secret';
$phpmailer->SMTPSecure = PHPMailerPHPMailerPHPMailer::ENCRYPTION_STARTTLS;
});
Debug SMTP:
add_action( 'phpmailer_init', function( $phpmailer ) {
$phpmailer->SMTPDebug = 2; // 0=off, 1=client, 2=client+server
$phpmailer->Debugoutput = function( $str, $level ) {
error_log( "PHPMailer [$level]: $str" );
};
});
Add DKIM Signing:
add_action( 'phpmailer_init', function( $phpmailer ) {
$phpmailer->DKIM_domain = 'example.com';
$phpmailer->DKIM_private = '/path/to/private.key';
$phpmailer->DKIM_selector = 'mail';
$phpmailer->DKIM_identity = $phpmailer->From;
});
wp_mail_succeeded
Fires after PHPMailer successfully sends an email.
do_action( 'wp_mail_succeeded', array $mail_data )
Since: 5.9.0
| Parameter | Type | Description |
|---|---|---|
$mail_data |
array | The email data |
$mail_data keys:
to(array) – Recipient email addressessubject(string) – Email subjectmessage(string) – Message bodyheaders(array) – Email headersattachments(array) – Attachment pathsembeds(array) – Embedded file paths
Note: Success means PHPMailer processed the request without errors. It does not guarantee delivery.
add_action( 'wp_mail_succeeded', function( $data ) {
error_log( 'Email sent to: ' . implode( ', ', $data['to'] ) );
});
wp_mail_failed
Fires when wp_mail() encounters an error.
do_action( 'wp_mail_failed', WP_Error $error )
Since: 4.4.0
| Parameter | Type | Description |
|---|---|---|
$error |
WP_Error | Error with message and mail data |
Error data keys:
to,subject,message,headers,attachments,embedsphpmailer_exception_code– PHPMailer exception code
add_action( 'wp_mail_failed', function( $error ) {
error_log( 'Email failed: ' . $error->get_error_message() );
error_log( 'To: ' . print_r( $error->get_error_data()['to'], true ) );
});
Multisite-Specific
fix_phpmailer_messageid
Hooked to phpmailer_init on multisite to set the Message-ID hostname.
// In ms-default-filters.php
add_action( 'phpmailer_init', 'fix_phpmailer_messageid' );
// In ms-functions.php
function fix_phpmailer_messageid( $phpmailer ) {
$phpmailer->Hostname = get_network()->domain;
}
Since: MU (3.0.0)
This ensures emails from multisite use the network domain in Message-ID headers rather than the server hostname.
Common Patterns
Force HTML Emails
add_filter( 'wp_mail_content_type', fn() => 'text/html' );
Custom From Address Site-Wide
add_filter( 'wp_mail_from', fn() => '[email protected]' );
add_filter( 'wp_mail_from_name', fn() => get_bloginfo( 'name' ) );
Log All Emails
add_action( 'wp_mail_succeeded', function( $data ) {
global $wpdb;
$wpdb->insert( 'email_log', [
'to' => implode( ', ', $data['to'] ),
'subject' => $data['subject'],
'sent_at' => current_time( 'mysql' ),
]);
});
add_action( 'wp_mail_failed', function( $error ) {
global $wpdb;
$data = $error->get_error_data();
$wpdb->insert( 'email_log', [
'to' => implode( ', ', $data['to'] ),
'subject' => $data['subject'],
'error' => $error->get_error_message(),
'sent_at' => current_time( 'mysql' ),
]);
});
Prevent Emails in Development
add_filter( 'pre_wp_mail', function( $return, $atts ) {
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
error_log( 'Email blocked: ' . $atts['subject'] );
return true; // Pretend success
}
return $return;
}, 10, 2 );
Redirect All Emails (Testing)
add_filter( 'wp_mail', function( $args ) {
$args['to'] = '[email protected]';
$args['subject'] = '[TEST] ' . $args['subject'];
return $args;
});