[cPanel][WordPress] Default mailing using the server’s hostname and the SPF issue

When managing shared hosting environments based on cPanel/WHM, I’ve often seen users encountering mailing issues because of misconfiguration that can make email functionality a bit of a headache: SPF record failures. These failures cause emails to be rejected by recipient services—a major frustration, especially for WordPress users.


Linux Sysadmin Tales

SysAdmin Troubleshooting Tales is a category of articles designed to share knowledge with junior sysadmins. Dive into the world of Linux system administration with straightforward articles that unravel the intricacies of troubleshooting. Learn practical solutions, tips, and tricks to tackle both common and uncommon sysadmin challenges. Whether you’re a seasoned professional or an aspiring tech enthusiast, this blog offers invaluable insights into the art of server management and problem-solving.


 


The Problem: Mismatched Sender Address and SPF Record

In a typical cPanel/WHM shared hosting setup, the server hostname might look something like cluster-5e8d1.tld or a subdomain such as cluser-698445.company.tld.  this server includes many accounts/users, each with a domain and maybe a different or shared IP addressed such as domain1.com, domain2.com, domain3.com, etc. Now, when a cPanel user installs WordPress either manually, or through WP Toolkit or Softaculous, their default email addresses are often like: [email protected] or [email protected].

However, when WordPress sends emails, it uses PHP’s mail() function, with the actual headers often looking like this:

This mismatch occurs because the FORM header isn’t explicitly defined at the level of wp_mail() which uses the native PHP mail() function, which again relies on the system exim sendmail. On the other hand, if Exim is not explicitly provided with a FORM header (i.e.sendmail_from value) —it falls back to a form including the username from which the command was executed at the hostname, something similar to using $USER or $(whoami)@$(hostname). The problem? The server hostname (cluster1.com) may not include the required SPF records for the IP of the domain (wpdomain.com). This discrepancy results in emails being flagged as suspicious or outright rejected by recipient mail services.

grep sendmail /opt/alt/php74/etc/php.ini ; For Win32 only.;sendmail_from = [email protected]; For Unix only.  You may supply arguments as well (default: "sendmail -t -i").sendmail_path = /usr/sbin/sendmail -t -i

 


Exploring Solutions

After understanding the root cause of the issue, we can easily identify three potential solutions, each with its own scope and implications:

1. Adding SPF Records for the Server Hostname

One simple solution is to include the IP addresses used for sending emails in the server hostname’s SPF record. For example, adding this TXT record to cluster1.com:

v=spf1 include:_spf.company.tld ~all

This ensures emails sent from [email protected] comply with SPF requirements. While straightforward, this approach still uses the server’s hostname, which may not align with users’ preferences or the company’s policy in allowing the users to safely use its hostname in sending forged emails with a custom from like [email protected].

2. Using SMTP for Outgoing Emails

Another option is to encourage WordPress users to configure SMTP for outgoing emails. By setting up an SMTP plugin (such as WP Mail SMTP), users can send emails directly from their own domain. This approach bypasses the server hostname issue entirely; however, it’s impractical for managing large numbers of users across various web solutions.

3. Enforcing the Use of Domain-Specific Senders

The third solution involves configuring PHP to enforce the use of a domain-specific “FROM” header, ensuring compatibility with SPF records. Here’s how this can be implemented:

  • In .php.ini or .user.ini:
    Add the following line to define a default sender:
    sendmail_from = "[email protected]" 
    which can also substituted through .htaccess
  • In wp-config.php (for WordPress):
    Inject the setting programmatically using:
    ini_set('sendmail_from', '[email protected]');
  • Using a WordPress filter:
    Modify the sender address dynamically:
    add_filter('wp_mail_from', function($email) {    return '[email protected]';});

Automating the Process: My Approach

Given that shared hosting environments can have dozens (or hundreds) of accounts, I needed an automated way to apply this configuration for all users. Here’s what I came up with:

Automated Script to Inject sendmail_from

Updating .htaccess files for each cPanel user with the appropriate sendmail_from setting:


 

#!/bin/bash# Script path: /myscripts/php_sendmail_injector.sh# This script updates .htaccess files for cPanel users by injecting# a "sendmail_from" configuration, ensuring proper email handling when using PHP mail()# TODO: I remember that this issue is also found in python/Django, to check# Logs actions to a file for easy tracking:LOG_FILE="/myscripts/logs/sendmail_injector.log"# Function to print to both console and log fileprint_log() {  local message="$1"  echo "$message"   # Print to console  echo "$(date '+%Y-%m-%d %H:%M:%S') - $message" >> "$LOG_FILE"  # Log with timestamp}print_log "********** PHP Sendmail Injector was run **********"# Get list of cPanel users and their domainswhmapi1 listaccts --output=json | jq -r '.data.acct[] | "\(.user) \(.domain)"' | while read -r cpuser domain; do  # Check if both cPanel user and domain are non-empty  if [[ -n "$cpuser" && -n "$domain" ]]; then    htaccess_path="/home/$cpuser/.htaccess"    # Check if .htaccess exists and ensure it doesn't already contain sendmail_from    if [[ ! -f "$htaccess_path" ]] || ! grep -q "sendmail_from" "$htaccess_path"; then      # Add sendmail_from line to the .htaccess file      echo "php_value sendmail_from \"noreply@$domain\"" >> "$htaccess_path"      chown "$cpuser":"$cpuser" "$htaccess_path"      chmod 644 "$htaccess_path"      print_log "Injected: sendmail_from to $htaccess_path for $cpuser ($domain)"    #else      # print_log "Skipped: $htaccess_path already exists and contains sendmail_from"    fi  fidoneexit 0

This script automates the following:

  1. Retrieves all cPanel users and their associated domains.
  2. Checks if the .htaccess file exists for each user and whether it already contains the sendmail_from directive.
  3. If not, appends the directive using the user’s domain.

It can be set as scheduled cron task and set up initially as a hook at the creation of a new account.


 

Comments

Leave a Reply

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