By default WordPress sends email using PHP’s mail() function, which passes the message to the server’s local mail transfer agent (MTA). On shared hosting this often means email arrives in spam, bounces silently, or fails to send at all — because the sending IP has no SPF or DKIM record associated with the sender domain, and many receiving mail servers reject it outright. The correct fix is to route outgoing email through a proper SMTP server — your hosting provider’s SMTP service, Gmail’s SMTP relay, a transactional email service like SendGrid or Mailgun, or your own mail server. WordPress provides the phpmailer_init action (renamed to PHPMailer\PHPMailer\PHPMailer in WP 5.5) which gives direct access to the PHPMailer object before a message is sent. Setting the SMTP credentials here affects all outgoing email — core notifications, WooCommerce order emails, Contact Form 7, and everything else that uses wp_mail() — without installing a dedicated plugin.
Problem: WordPress emails are going to spam or failing to deliver because the server's local mail function has no authenticated sending credentials for your domain.
Solution: Hook into phpmailer_init and configure the PHPMailer object to use your SMTP server with authentication. Store credentials in wp-config.php constants — not in the database.
Define credentials in wp-config.php (above the /* That's all, stop editing! */ line):
<?php
// SMTP configuration — add to wp-config.php
define( 'SMTP_HOST', 'smtp.example.com' );
define( 'SMTP_PORT', 587 );
define( 'SMTP_SECURE', 'tls' ); // 'tls' (STARTTLS on 587) or 'ssl' (SMTPS on 465)
define( 'SMTP_AUTH', true );
define( 'SMTP_USERNAME', 'user@example.com' );
define( 'SMTP_PASSWORD', 'your-smtp-password' );
Add to functions.php or a mu-plugin:
<?php
add_action( 'phpmailer_init', 'configure_smtp_mailer' );
function configure_smtp_mailer( $phpmailer ) {
$phpmailer->isSMTP();
$phpmailer->Host = defined( 'SMTP_HOST' ) ? SMTP_HOST : '';
$phpmailer->Port = defined( 'SMTP_PORT' ) ? SMTP_PORT : 587;
$phpmailer->SMTPSecure = defined( 'SMTP_SECURE' ) ? SMTP_SECURE : 'tls';
$phpmailer->SMTPAuth = defined( 'SMTP_AUTH' ) ? SMTP_AUTH : true;
$phpmailer->Username = defined( 'SMTP_USERNAME' ) ? SMTP_USERNAME : '';
$phpmailer->Password = defined( 'SMTP_PASSWORD' ) ? SMTP_PASSWORD : '';
// Override From address (optional — ensures From matches the authenticated domain)
$phpmailer->setFrom( SMTP_USERNAME, get_bloginfo( 'name' ) );
}
// Test the configuration by temporarily logging errors
add_action( 'wp_mail_failed', function ( $error ) {
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
error_log( 'wp_mail failed: ' . $error->get_error_message() );
}
} );
NOTE: Never store SMTP credentials directly in functions.php or any file tracked by version control. Keep them in wp-config.php (which is typically excluded from repos) or in environment variables read at runtime with getenv(). If you use a managed WordPress host that restricts phpmailer_init, a transactional email plugin (WP Mail SMTP, Post SMTP) is the correct alternative — they expose the same configuration through a UI rather than code.