How To Find Out The Source Of Spam Emails On Your WordPress Site

If your WordPress site sends out spam emails, you will need to find out where the cause is located. Use this script to log activity of the wp-mail function.

The most common spam problem with WordPress sites is incoming spam. It usually reaches your comment and contact forms. There are various plugins and services that help you reduce that spam. I won’t deal with that issue here.

A not less delicate problem, however, is spam that is sent from your site. Your site might have been hacked or one of your plugins or themes has a security bug. A symptom is a huge number of mail being sent out from your site. Your hosting company or system admin might therefore send you a warning. Or they may even block the PHP function to send out mails. If you receive an undeliverable mail from your site, you can often clearly identify it as spam.

Of course, you first need to identify the precise location of the cause before you can fix it. And here hosting companies often don’t give you much useful information.

Photo by anSICHThoch3 (Pixabay)

Log emails sent by WordPress

I therefore created a short script that can you can paste for example into the functions.php of your active (child) theme. It doesn’t solve any problems. It only helps you debug. It intercepts the WordPress wp-mail function and logs the output to a file. After each entry it adds a backtrace that shows you what happened before the mail was sent.

The size of the output file is limited to 50 KB. You could also forward these emails to your own address but I prefer to write them to a file in case the webhosting has disabled sending mails.

function cm_log_mail( $variables ) {

  $upload_dir = wp_upload_dir();

  if ( ! empty( $upload_dir['basedir'] ) ) {

    $path_to_file = $upload_dir['basedir'] . '/mails-log.txt'; // or choose your own file name

    if ( filesize( $path_to_file ) < 1024 * 50 ) { // set here the max size

      debug_print_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ); // remove DEBUG_BACKTRACE_IGNORE_ARGS to log all parameters, including mail bodies, which can be large
      $trace = ob_get_contents();

      $text =  "\n\n";
      $text .= 'To: ' . $variables['to'] . "\n";
      $text .= 'Subject: ' . $variables['subject'] . "\n";
      $text .= 'Backtrace: ' . $trace . "\n";

      $text .= '===========================' . "\n";

      file_put_contents( $path_to_file, $text, FILE_APPEND );




add_filter( 'wp_mail', 'cm_log_mail', 1 );

The output can be viewed through an URL. (That’s why you need to disable the additional lines and remove the log file after you finished debugging.)

Here is some sample output after I sent a test mail with WP Mail SMTP. The important things happen starting with the line “wp_mail() called at ...“.1

To: [email protected]
Subject: WP Mail SMTP: HTML Test email to [email protected]
Backtrace: #0  catch_mail() called at [/path-to-wp/wp-includes/class-wp-hook.php:286]
#1  WP_Hook->apply_filters() called at [/path-to-wp/wp-includes/plugin.php:208]
#2  apply_filters() called at [/path-to-wp/wp-includes/pluggable.php:186]
#3  wp_mail() called at [/path-to-wp/wp-content/plugins/wp-mail-smtp/src/Admin/Pages/Test.php:167]
#4  WPMailSMTP\Admin\Pages\Test->process_post() called at [/path-to-wp/wp-content/plugins/wp-mail-smtp/src/Admin/Area.php:646]
#5  WPMailSMTP\Admin\Area->process_actions() called at [/path-to-wp/wp-includes/class-wp-hook.php:286]
#6  WP_Hook->apply_filters() called at [/path-to-wp/wp-includes/class-wp-hook.php:310]
#7  WP_Hook->do_action() called at [/path-to-wp/wp-includes/plugin.php:465]
#8  do_action() called at [/path-to-wp/wp-admin/admin.php:169]


You see that sending was triggered somewhere at .../plugins/wp-mail-smtp/... and you can even check the precise code. The numbers behind the names of PHP files are line numbers.

Of course, this script only logs activities that make use of wp-mail. If you don’t see anything suspicious here, something might still use PHP’s mail or PHPMailer directly.2

Don’t forget to remove the debugging information

Since the file mails-log.txt (or however you named it) is publicly accessible, you should remove the code from the functions.php (or comment out the add_filter part) after you’re done debugging and then delete the log file too.

Photo by anSICHThoch3 (Pixabay)

Show 2 footnotes
  1. The backtrace is ordered chronologically with the oldest events at the bottom.
  2. Then it becomes more complicated. Check out Xdebug.

Check Out My Plugins:

Organize your tags in coherent groups and tidy up your tag cloud! Display your tags under tabs or in an accordion.

You will never want to go back to the times when your tags were all over the place.


Supercharge your tags! Tag your posts on two levels with tag groups. Filter your posts on the frontend. Let your visitors search in real time by tags and groups.

Go directly to your first, latest or a random post just by adding some magic words to your site's URL. Search posts by author, or category, or something else. Or combine a time frame with search parameters.


Leave a Reply

Your email address will not be published. Required fields are marked *