WordPress Comment Moderation Hooks Example: Reduce Spam Signals Safely
Use WordPress comment moderation hooks to hold risky comments, log useful signals, and reduce spam false positives.
Published
April 23, 2026
Reading Time
2 min read
Updated
April 23, 2026

Hardening Notes
Baselines, access reduction, and default settings that stand up in production.
Best For
Teams preparing, launching, or maintaining WordPress as a backend service in a production stack.
Primary Topics
Editorial Focus
Control Ledger: Baselines, access reduction, and default settings that stand up in production. Updated on April 23, 2026.
Full Report
Last reviewed: April 23, 2026
Comment spam is not only a moderation problem. On busy WordPress sites it becomes a database, notification, reputation, and support problem. Default settings help, but custom projects often need site-specific rules for promotional links, suspicious keywords, or abuse patterns.
This guide shows how to use comment moderation hooks to send risky comments to moderation, log useful signals, and avoid blocking legitimate readers with fragile string matching.
Moderate before the comment is approved
<?php
add_filter( 'pre_comment_approved', 'vulnwp_moderate_risky_comments', 20, 2 );
function vulnwp_moderate_risky_comments( $approved, array $commentdata ) {
$content = isset( $commentdata['comment_content'] ) ? (string) $commentdata['comment_content'] : '';
$author_url = isset( $commentdata['comment_author_url'] ) ? (string) $commentdata['comment_author_url'] : '';
$link_count = preg_match_all( '/https?:\/\//i', $content );
$has_author_url = '' !== trim( $author_url );
if ( $link_count >= 2 || ( $has_author_url && $link_count >= 1 ) ) {
return 0;
}
if ( vulnwp_contains_blocked_phrase( $content ) ) {
return 'spam';
}
return $approved;
}
The filter can return approved, pending, spam, trash, or a WP_Error depending on your policy. For most editorial sites, sending suspicious comments to moderation is safer than deleting aggressively.
Keep phrase matching conservative
function vulnwp_contains_blocked_phrase( $content ) {
$phrases = array(
'free crypto bonus',
'guaranteed casino system',
'cheap backlinks package',
);
$normalized = strtolower( wp_strip_all_tags( $content ) );
foreach ( $phrases as $phrase ) {
if ( str_contains( $normalized, $phrase ) ) {
return true;
}
}
return false;
}
Use phrases that are specific enough to avoid false positives. Broad words such as “SEO”, “plugin”, or “support” will catch legitimate comments.
Log signals after insertion
add_action( 'comment_post', 'vulnwp_log_moderated_comment_signal', 10, 3 );
function vulnwp_log_moderated_comment_signal( $comment_id, $comment_approved, $commentdata ) {
if ( 1 === (int) $comment_approved ) {
return;
}
error_log(
sprintf(
'Comment %d held with status %s from IP %s',
(int) $comment_id,
(string) $comment_approved,
isset( $commentdata['comment_author_IP'] ) ? sanitize_text_field( $commentdata['comment_author_IP'] ) : 'unknown'
)
);
}
Do not log full comment bodies if they can contain private information. Log IDs and signals, then review the comment in WordPress.
Handle headless WordPress carefully
Headless sites often submit comments through custom front-end forms or REST routes. The moderation rules should live in WordPress so they apply no matter which front end sends the request. If the front end duplicates rules, treat that as user experience only, not the source of truth.
Production checklist
- Keep moderation rules in WordPress, not only in JavaScript.
- Prefer pending moderation for uncertain signals.
- Use spam or trash only for high-confidence patterns.
- Log IDs and moderation status, not full private content.
- Review false positives weekly after deploying new rules.
- Pair custom rules with native Discussion settings and an anti-spam plugin when traffic grows.
Common mistakes
- Blocking too broadly. Overbroad keyword checks punish real readers.
- Trusting front-end validation. Attackers can post directly to WordPress endpoints.
- Logging private content. Moderation logs should not become a second comment database.
- No feedback loop. Rules need adjustment based on false positives and missed spam.
- Deleting everything suspicious. Pending moderation is often the better default.
Related reading
For request authentication patterns, see the webhook receiver guide. If your comment form uses custom endpoints, start with the register_rest_route example.


