2026年11月12日

2026年11月12日

Send WordPress Email via SMTP: phpmailer_init and WP Mail SMTP

This article is a localized edition of the Japanese original. Read the Japanese original

WordPress sends email such as contact form notifications and password resets with wp_mail(). By default it uses PHP's mail() function on the server (in practice, the server's mail-sending program), so depending on how the sending server and domain are configured, messages may not arrive or may land in spam.

If you route sending through an authenticated SMTP server (your mail server, Gmail, or an email delivery service), mail leaves from a server authenticated as the sender, which improves delivery. This article covers both a plugin-free method and WP Mail SMTP.

Check before switching to SMTP

  • Sender address domain: use an address on your own domain as the sender (From). If that domain's SPF and DKIM do not cover the SMTP server you use, mail can still be marked as spam even via SMTP
  • SMTP connection details: look up the host name, port, encryption (STARTTLS or SSL/TLS), username and password (or API key) in the admin screen of your mail server or service
  • Port: usually 587 (STARTTLS) or 465 (SSL/TLS). Some servers restrict outbound connections, so if you cannot connect, check your hosting company's documentation

Method 1: Configure without a plugin (phpmailer_init)

Right before sending with wp_mail(), WordPress runs the phpmailer_init action and passes the PHPMailer object used for sending. Configuring SMTP there routes all mail through SMTP.

Put the connection details in wp-config.php

Write passwords and API keys as constants in wp-config.php, not in theme files. Theme files are easily exposed through theme distribution or the file editor, and the settings disappear when you switch themes.

<?php
// Add to wp-config.php above the "That's all, stop editing!" line
define( 'SMTP_HOST', 'smtp.example.com' );
define( 'SMTP_PORT', 587 );
define( 'SMTP_SECURE', 'tls' );            // 'tls' (STARTTLS) for 587, 'ssl' for 465
define( 'SMTP_USER', 'noreply@example.com' );
define( 'SMTP_PASS', 'your-password-or-api-key' );
define( 'SMTP_FROM', 'noreply@example.com' );
define( 'SMTP_FROM_NAME', 'Site Name' );

The names starting with SMTP_ are chosen for the code in this article. WordPress core does not read them.

Put the sending code in an mu-plugin

To keep the code independent of the theme, make it an mu-plugin (a must-use plugin that is always loaded without activation). Create the wp-content/mu-plugins/ folder if it does not exist and place smtp.php with the following content. An mu-plugin is active as soon as the file is in place.

<?php
/**
 * Plugin Name: SMTP sending settings
 * Description: Sends wp_mail() via SMTP using the SMTP_* constants in wp-config.php.
 */

add_action( 'phpmailer_init', function ( $phpmailer ) {
    if ( ! defined( 'SMTP_HOST' ) ) {
        return; // Do nothing without the constants (keep sending with mail())
    }

    $phpmailer->isSMTP();
    $phpmailer->Host       = SMTP_HOST;
    $phpmailer->Port       = defined( 'SMTP_PORT' ) ? SMTP_PORT : 587;
    $phpmailer->SMTPSecure = defined( 'SMTP_SECURE' ) ? SMTP_SECURE : 'tls';
    $phpmailer->SMTPAuth   = true;
    $phpmailer->Username   = SMTP_USER;
    $phpmailer->Password   = SMTP_PASS;
} );

// Sender address and name (default is wordpress@your-site-domain)
add_filter( 'wp_mail_from', function ( $email ) {
    return defined( 'SMTP_FROM' ) ? SMTP_FROM : $email;
} );

add_filter( 'wp_mail_from_name', function ( $name ) {
    return defined( 'SMTP_FROM_NAME' ) ? SMTP_FROM_NAME : $name;
} );

// When sending fails, write the reason to the error log
add_action( 'wp_mail_failed', function ( $error ) {
    error_log( 'wp_mail failed: ' . $error->get_error_message() );
} );

The sender is changed with the wp_mail_from and wp_mail_from_name filters. Without them, the sender is wordpress@your-site-domain, an address that does not exist. wp_mail_failed is an action that runs when sending fails, so you can log the error returned by the SMTP server.

Method 2: Configure with WP Mail SMTP

If you prefer not to write code, use the "WP Mail SMTP" plugin. Search for it under Plugins → Add New Plugin, install it, then in the setup wizard or under WP Mail SMTP → Settings choose "Other SMTP" as the mailer and enter the host, encryption, port, username and password.

A password entered in the admin screen is stored in the database. To avoid that, define constants in wp-config.php; those settings then use the constant values and cannot be changed from the screen.

<?php
// Add to wp-config.php (configures WP Mail SMTP's "Other SMTP" with constants)
define( 'WPMS_ON', true );
define( 'WPMS_MAILER', 'smtp' );
define( 'WPMS_SMTP_HOST', 'smtp.example.com' );
define( 'WPMS_SMTP_PORT', 587 );
define( 'WPMS_SSL', 'tls' );               // 'tls', 'ssl', or '' for no encryption
define( 'WPMS_SMTP_AUTH', true );
define( 'WPMS_SMTP_USER', 'noreply@example.com' );
define( 'WPMS_SMTP_PASS', 'your-password-or-api-key' );
define( 'WPMS_MAIL_FROM', 'noreply@example.com' );
define( 'WPMS_MAIL_FROM_NAME', 'Site Name' );

Using the Method 1 code and WP Mail SMTP at the same time makes it hard to tell which settings are in effect. Use only one of them.

Connection details for common SMTP services

Service Host Port / encryption Username Password
Gmail smtp.gmail.com 587 (STARTTLS) or 465 (SSL) Your Gmail address App password
SendGrid smtp.sendgrid.net 587 (STARTTLS) The fixed string apikey Your SendGrid API key
Amazon SES (Tokyo region) email-smtp.ap-northeast-1.amazonaws.com 587 (STARTTLS) Username of your SES SMTP credentials Password of your SES SMTP credentials

Using Gmail

Use a 16-digit "app password", not your Gmail password itself. Turn on 2-Step Verification for the Google account, then create one at https://myaccount.google.com/apppasswords. You may not be able to create app passwords if 2-Step Verification uses only security keys, if Advanced Protection is on, or for work, school or other organization accounts (according to Google's help as of October 2026).

When sending through Gmail, if the sender differs from the address you log in with, Gmail may rewrite the sender. For sites that send a lot of mail from an address on their own domain, an email delivery service like the ones below is a better fit.

Using SendGrid or Amazon SES

SendGrid uses the fixed string apikey as the username and your API key as the password. Amazon SES uses "SMTP credentials" created in the SES console; note that these are different from AWS access keys and IAM user passwords. With both services, verify your sending domain or address on the service side first. A new Amazon SES account starts in a restricted state called the sandbox and can send only to verified addresses.

Test sending

If you can use WP-CLI, this command sends a test message to the administrator's email address. If it prints bool(true), WordPress successfully handed the message to the SMTP server.

wp eval 'var_dump( wp_mail( get_option( "admin_email" ), "SMTP test", "This is a test." ) );'

If you get bool(false), look for the wp_mail failed: line logged by the Method 1 code in the error log. With WP Mail SMTP, you can also send from the "Email Test" tab under WP Mail SMTP → Tools.

If you get bool(true) but the message never arrives, it was stopped during delivery after the SMTP server accepted it. Check the spam folder, the SPF and DKIM settings of the sending domain, and the service's sending logs.

Debugging when it does not work

To see the conversation with the SMTP server, temporarily enable PHPMailer's debug output. Add the following code to the mu-plugin, and add define( 'SMTP_DEBUG', true ); to wp-config.php only while investigating.

<?php
// Enable only while investigating by adding define( 'SMTP_DEBUG', true ); to wp-config.php
add_action( 'phpmailer_init', function ( $phpmailer ) {
    if ( ! defined( 'SMTP_DEBUG' ) || ! SMTP_DEBUG ) {
        return;
    }
    $phpmailer->SMTPDebug   = 2;           // Output the client-server conversation
    $phpmailer->Debugoutput = 'error_log'; // To the error log, not the screen
}, 20 );

The output goes to the PHP error log. If WP_DEBUG and WP_DEBUG_LOG are enabled in wp-config.php, it is written to wp-content/debug.log. Common errors:

  • SMTP connect() failed: wrong host or port, or the server cannot make outbound connections
  • Could not authenticate: wrong username or password (API key). For Gmail, check that you are using an app password
  • Certificate errors: the port and encryption do not match (587 with tls, 465 with ssl)

When you are done, remove the SMTP_DEBUG definition (or set it to false) and delete the log file. If you leave debug output on, the log keeps growing with every message sent.

Sending HTML email

The simplest way to send HTML email is to pass a header as the fourth argument of wp_mail(). If you use the wp_mail_content_type filter, attach a named function rather than an anonymous one and remove it by the same name after sending. remove_filter() only works when given the same function that was added (a new anonymous function with the same body is treated as a different function).

<?php
// Option 1: set it in the headers (no filter, so nothing to clean up)
function myprefix_send_html_mail( $to, $subject, $html_body ) {
    $headers = [ 'Content-Type: text/html; charset=UTF-8' ];
    return wp_mail( $to, $subject, $html_body, $headers );
}

// Option 2: with a filter, add a named function and remove it by the same name
function myprefix_html_content_type() {
    return 'text/html';
}

function myprefix_send_html_mail_with_filter( $to, $subject, $html_body ) {
    add_filter( 'wp_mail_content_type', 'myprefix_html_content_type' );
    $result = wp_mail( $to, $subject, $html_body );
    remove_filter( 'wp_mail_content_type', 'myprefix_html_content_type' );

    return $result;
}

If you leave a filter that always returns text/html for wp_mail_content_type, WordPress core's plain-text emails such as password resets are treated as HTML too, and line breaks disappear.

Cautions

  • Keep passwords and API keys in wp-config.php, never in theme files or a public repository. Also set the permissions of wp-config.php so that only the web server can read it
  • After changing SMTP settings, check that each email your site actually sends, such as contact form notifications, still arrives
  • Disable code that enables SMTPDebug and any test code once you are done investigating

Summary

  • wp_mail() uses the server's mail() by default. If mail does not arrive, route it through an authenticated SMTP server
  • Without a plugin, use constants in wp-config.php and an mu-plugin that hooks phpmailer_init. With a plugin, use WP Mail SMTP, which can also take the password from constants
  • Set the sender to an address on your domain with wp_mail_from, and set up SPF and DKIM for that domain
  • Check the result with wp eval and wp_mail_failed, and enable SMTPDebug only when needed

Related articles

Questions about WordPress? The publisher of this site is based in Japan and replies in Japanese. Contact (Japanese)

お気軽にご相談ください

お見積りへ お問い合わせへ
▲