Files

3.2 KiB

WPForms Email Delivery Debugging

Common Failure Modes on SiteGround/RunCloud WordPress Hosting

1. PHP Serialization Length Mismatch

Symptom: WP Mail SMTP plugin shows "Authentication failed" or silently drops emails. Debug log shows SMTP connection succeeds but auth fails.

Root cause: When the SMTP password is updated via the database (not the plugin UI), the PHP serialized string length in wp_options may not match the actual password length.

Example: password apex.track!! (13 chars) stored as s:72:"apex.track!!" (declares 72 chars). PHP unserialization fails and the password field comes back empty.

Check:

SELECT LENGTH(JSON_UNQUOTE(JSON_EXTRACT(option_value, '$.smtp.pass'))) as pass_len
FROM wp_options WHERE option_name = 'wp_mail_smtp';

If NULL, the serialization is broken.

Fix:

UPDATE wp_options 
SET option_value = REPLACE(option_value, 
  's:72:\"apex.track!!\"', 
  's:13:\"apex.track!!\"')
WHERE option_name = 'wp_mail_smtp';

Replace s:72 with s:N where N = actual character count of the password.

2. Invalid Sender Address (Multiple Emails)

Symptom: SMTP server rejects with "X domain is not currently owned by sender" or similar. WPForms notifications specify a sender_address that may differ from the SMTP login.

Check:

SELECT JSON_EXTRACT(post_content, '$.settings.notifications."1".sender_address')
FROM wp_posts WHERE ID = <form_id>;

Fix: Ensure sender_address is a single email matching the SMTP login user. If it contains comma-separated emails, remove extras. The notification's email field handles multiple recipients — sender_address is the From: header only.

UPDATE wp_posts 
SET post_content = REPLACE(post_content, 
  'sender_address\":\"contact@site.com, admin@site.com',
  'sender_address\":\"contact@site.com')
WHERE ID IN (<form_ids>);

3. Diagnostic Script

# Check SMTP connectivity from WordPress host
echo | openssl s_client -connect <smtp_host>:2525 -starttls smtp 2>&1 | grep -c 'CONNECTED'

# Test SMTP login
python3 -c "
import smtplib, ssl
ctx = ssl.create_default_context()
with smtplib.SMTP('<host>', 2525, timeout=15) as s:
    s.starttls(context=ctx)
    s.login('<user>', '<pass>')
    msg = 'From: <from>\\nTo: <to>\\nSubject: Test\\n\\nBody'
    s.sendmail('<from>', ['<to>'], msg)
print('OK')
"

# Check WP Mail SMTP debug events for recent failures
mysql -u <db_user> -p'<db_pass>' <db_name> -e "
SELECT COUNT(*) FROM wp_wpmailsmtp_debug_events 
WHERE event_type = 0 AND created_at >= NOW() - INTERVAL 10 MINUTE;
"

# Full debug log for recent errors
mysql -u <db_user> -p'<db_pass>' <db_name> -e "
SELECT id, content FROM wp_wpmailsmtp_debug_events 
WHERE event_type = 0 ORDER BY id DESC LIMIT 3\G
"

4. Watchdog Pattern (every 5 min)

The apex-mail-watchdog.sh script runs as a no_agent cron on Core, SSHing into the WordPress host to:

  1. Send a test email via Python SMTP (silent on success)
  2. Check wp_wpmailsmtp_debug_events for recent failures (silent on success)
  3. On failure: alerts the user via Hermes cron's delivery mechanism

Key detail for the watchdog: on success, the script exits 0 with NO stdout. Only failures produce output. This keeps the user's chat clean.