O TYM ODCINKU
Disabling comments in WordPress takes more than unchecking one setting. You have to close comments on the posts you already published, remove comment support in code, decide what to do with the comments already in your database, and close the registration form if you opened it for commenters.
- Discussion settings only apply to new posts. Close existing posts with Bulk Edit or WP-CLI before you add any code.
- A small must-use plugin closes comments and pings, hides existing comments, removes the admin screens, and closes the comments REST route to the public.
- Back up before deleting comments. Block editor Notes and WooCommerce reviews live in the same table.
- If you turned on open registration so people could comment, turn it off. Single-site WordPress accepts usernames that spammers are using to send phishing text through your site.
In 2023, I deleted every comment on Martech Zone and disabled comments in my child theme. The site still runs without them. This update replaces the code I shared then with a version I tested against WordPress 7.1.4, adds the cleanup commands I use with WP-CLI, and covers a registration problem that Matt Mullenweg raised on the Make WordPress Core blog on November 8, 2026. If you ever opened registration so readers could log in and comment, read that section first.
- Why I Removed Comments From Martech Zone
- The Open Registration Hole to Close First
- Turn Off Comments in the Discussion Settings
- Close Comments on Posts You Already Published
- Disable Comments Site-Wide With a Must-Use Plugin
- Classic Themes and Block Themes
- Delete Existing Comments
- When a Plugin Is Simpler
- How I Tested This Code
- Spam prevention: Comments on WordPress sites attract spam. Filtering it takes time, and anything that slips through clutters the site and hurts its reputation. Disabling comments removes the problem entirely.
- Broken avatars: As I crawled the site for issues, one that kept cropping up was commenters who had abandoned their Gravatar profile, the service WordPress uses to display a commenter’s avatar. Instead of a clean default image, some of those requests produced file not found errors that slowed pages down. Fixing them one commenter at a time was not worth it.
- Link quality: Comments bring outbound links, and many point to low-quality or spammy sites. Search engines weigh the quality of the sites you link to. Removing comments keeps every link on the page one I chose, which matters for SEO.
- Time: Moderating comments drains time that is better spent writing, optimizing content, and running sales and marketing work.
- Conversations moved: Most discussion about an article now happens where it is shared, on platforms like LinkedIn, Facebook, and X. I would rather meet readers there.
A common way to fight comment spam is to check Users must be registered and logged in to comment under Settings > Discussion, then check Anyone can register under Settings > General so readers can create accounts. That combination publishes a registration form at wp-login.php?action=register that anyone, including bots, can submit.
On November 6, 2026, Joe Youngblood posted on X that a WordPress site he works on was being flooded with fake registrations. The spammers were putting a phishing message and a domain name in the username field and entering real people’s email addresses, so the site itself sent email carrying the spammer’s text. He noted the site already had a CAPTCHA on the form, Wordfence, and Cloudflare protections, and the registrations kept coming. He also pointed out the second cost: mailbox providers see phishing-style content coming from your domain and mail server, which can hurt delivery of the email you actually need to send.
Matt Mullenweg responded in a Make WordPress Core post titled Conservative in What We Accept. He wrote that the report surprised him because WordPress’s conservative username sanitization does not apply to registrations when a site is not running Multisite. He said WordPress should assume every CRUD attempt is hostile, suggested that new registrations could follow stricter username rules than before without breaking backward compatibility, and asked whether comment notification emails should leave out the comment content and simply link to the moderation page. The post is a proposal. It does not announce a patch, a version, or a release date.
I checked the WordPress 7.1.4 source to see what that means in practice. On Multisite, new usernames can only contain lowercase letters and numbers. On a single site, the registration form validates usernames with validate_username(), which also allows spaces, periods, hyphens, underscores, and the @ sign. A sentence followed by a domain name passes. When the account is created, core emails the site administrator a New user registration message with the username in plain text, and it emails whatever address was entered a password link that carries the username in the URL. Registration plugins and custom verification emails may print the username directly, which matches what Joe described.
- Turn it off if you do not need it. Uncheck Anyone can register in Settings > General. If you are disabling comments, there is no reason to leave it on.
- Confirm the default role. New User Default Role on the same screen should be Subscriber. Never assign a higher role to self-registered accounts.
- Review accounts created during an attack. List subscribers whose usernames contain anything other than lowercase letters and numbers, then review them before deleting.
- Harden registration if you must keep it. Apply the Multisite username rule to public sign-ups, stop the admin email for every registration, and keep comment text out of notification emails. A CAPTCHA such as Cloudflare Turnstile and a spam filter such as Akismet still help, but Joe’s experience shows they are not enough on their own.
- Keep WordPress updated and watch the Make WordPress Core blog for any change to how core handles new registrations.
# Check and close public registration
wp option get users_can_register
wp option update users_can_register 0
# New accounts should default to Subscriber
wp option get default_role
# Find subscriber usernames that would fail the Multisite rule
wp user list --role=subscriber --field=user_login | grep '[^a-z0-9]'
If you need open registration, this must-use plugin applies the Multisite username rule to the public form, stops the administrator email for every new account, and replaces the body of comment notification emails with a link to the comment in your dashboard. Save it as wp-content/mu-plugins/registration-hardening.php.
<?php
/**
* Plugin Name: Registration and Notification Hardening
* Description: Stricter usernames for public sign-ups and fewer spam-friendly emails.
*/
defined( 'ABSPATH' ) || exit;
// Apply Multisite's username rule (lowercase letters and numbers only) to public registrations.
add_filter(
'registration_errors',
function ( $errors, $sanitized_user_login, $user_email ) {
if ( '' !== $sanitized_user_login && preg_match( '/[^a-z0-9]/', $sanitized_user_login ) ) {
$errors->add( 'invalid_username', __( 'Usernames can only contain lowercase letters (a-z) and numbers.' ) );
}
return $errors;
},
10,
3
);
// Stop the "New user registration" email to the site admin (WordPress 6.1 and later).
add_filter( 'wp_send_new_user_notification_to_admin', '__return_false' );
// Leave comment text out of notification emails and link to the moderation screen instead.
function mysite_comment_email_without_content( $message, $comment_id ) {
$comment = get_comment( $comment_id );
if ( ! $comment ) {
return $message;
}
return sprintf(
"A comment on \"%s\" needs your attention.\r\n\r\nReview it here: %s\r\n",
wp_specialchars_decode( get_the_title( $comment->comment_post_ID ), ENT_QUOTES ),
admin_url( 'comment.php?action=editcomment&c=' . $comment->comment_ID )
);
}
add_filter( 'comment_moderation_text', 'mysite_comment_email_without_content', 10, 2 );
add_filter( 'comment_notification_text', 'mysite_comment_email_without_content', 10, 2 );
registration_errorsruns only for the public registration form inregister_new_user(). Accounts you create in the dashboard and existing users are not affected, so a current username with a period still logs in.- The error message reuses the exact string WordPress Multisite shows, so it is already translated in most languages.
wp_send_new_user_notification_to_adminhas been available since WordPress 6.1. The new user still gets their password link. Check the Users screen instead of your inbox for new sign-ups.comment_moderation_textandcomment_notification_textfilter the body of the held-for-moderation and new-comment emails. The function returns the original message if the comment cannot be loaded.
Go to Settings > Discussion and uncheck these three boxes under Default post settings:
- Attempt to notify any blogs linked to from the post
- Allow link notifications from other blogs (pingbacks and trackbacks) on new posts
- Allow people to submit comments on new posts
WordPress shows a note right below them: individual posts may override these settings, and changes only apply to new posts. Everything you already published keeps its current setting. The same three options from the command line:
wp option update default_comment_status closed
wp option update default_ping_status closed
wp option update default_pingback_flag 0
Close Comments on Posts You Already Published
Do this before you add any code. Once comment support is removed from a post type, the Bulk Edit panel no longer shows the Comments and Pings fields for it.
- Go to Posts > All Posts and use Screen Options to show more items per page.
- Select all, choose Edit from Bulk actions, and click Apply.
- Set Comments to Do not allow and Pings to Do not allow, then click Update.
- Repeat for Pages and any custom post types. For a single post, use the Discussion setting in the editor’s post sidebar.
With WP-CLI, this closes every open post, page, and custom post type in two commands:
wp post update $(wp post list --post_type=any --comment_status=open --format=ids) --comment_status=closed
wp post update $(wp post list --post_type=any --ping_status=open --format=ids) --ping_status=closed
Bulk Edit and wp post update both change the modified date on every post they touch, which can make your whole archive look freshly updated to feeds and sitemaps. If you want to avoid that, update the columns directly with a backup in hand:
wp db export before-closing-comments.sql
wp db query "UPDATE $(wp db prefix)posts SET comment_status = 'closed', ping_status = 'closed' WHERE comment_status = 'open' OR ping_status = 'open';"
wp cache flush
Disable Comments Site-Wide With a Must-Use Plugin
This is the updated version of the code I use. I moved it out of the theme and into a must-use plugin so it keeps working if the theme changes. Create the wp-content/mu-plugins folder if it does not exist and save this as disable-comments.php. WordPress loads must-use plugins automatically, and they cannot be deactivated from the Plugins screen. It also works in a child theme’s functions.php file.
<?php
/**
* Plugin Name: Disable Comments Site-Wide
* Description: Closes comments, pingbacks, and trackbacks and removes comment UI.
*/
defined( 'ABSPATH' ) || exit;
// Remove comment and trackback support from every registered post type.
add_action(
'init',
function () {
foreach ( get_post_types() as $post_type ) {
if ( post_type_supports( $post_type, 'comments' ) ) {
remove_post_type_support( $post_type, 'comments' );
}
if ( post_type_supports( $post_type, 'trackbacks' ) ) {
remove_post_type_support( $post_type, 'trackbacks' );
}
}
},
100
);
// Close comments and pings everywhere, including older posts still set to open.
add_filter( 'comments_open', '__return_false', 20 );
add_filter( 'pings_open', '__return_false', 20 );
// Hide existing comments and report zero so themes and the Comments block render nothing.
add_filter( 'comments_array', '__return_empty_array', 20 );
add_filter( 'get_comments_number', '__return_zero', 20 );
// Remove the Latest Comments block output and the Recent Comments widget.
add_filter( 'render_block_core/latest-comments', '__return_empty_string' );
add_action(
'widgets_init',
function () {
unregister_widget( 'WP_Widget_Recent_Comments' );
}
);
// Remove the comments feed link and send comment feed requests to the home page.
add_filter( 'feed_links_show_comments_feed', '__return_false' );
add_action(
'template_redirect',
function () {
if ( is_comment_feed() ) {
wp_safe_redirect( home_url( '/' ), 301 );
exit;
}
},
1
);
// Remove the Comments screen from the admin menu and redirect direct visits.
add_action(
'admin_menu',
function () {
remove_menu_page( 'edit-comments.php' );
}
);
add_action(
'admin_init',
function () {
global $pagenow;
if ( 'edit-comments.php' === $pagenow ) {
wp_safe_redirect( admin_url() );
exit;
}
}
);
// Remove the comments bubble from the admin bar (core adds it at priority 60).
add_action(
'admin_bar_menu',
function ( $wp_admin_bar ) {
$wp_admin_bar->remove_node( 'comments' );
},
100
);
// Stop accepting pingbacks over XML-RPC.
add_filter(
'xmlrpc_methods',
function ( $methods ) {
unset( $methods['pingback.ping'], $methods['pingback.extensions.getPingbacks'] );
return $methods;
}
);
// Keep the REST comments routes for editors (block editor Notes use them) but close them to everyone else.
add_filter(
'rest_pre_dispatch',
function ( $result, $server, $request ) {
if ( null !== $result ) {
return $result;
}
if ( 0 === strpos( $request->get_route(), '/wp/v2/comments' ) && ! current_user_can( 'edit_posts' ) ) {
return new WP_Error( 'rest_comments_disabled', 'Comments are disabled on this site.', array( 'status' => 403 ) );
}
return $result;
},
10,
3
);
What each part does:
- Post type support: Removes
'comments'and'trackbacks'support from every registered post type oninitat priority 100, after themes and plugins register their types. That removes the Discussion panel, the comment columns, and comment fields in the editor. - Open status:
comments_openandpings_openreturn false for every post, including old posts whose database setting still says open. Core uses these checks for the comment form,wp-comments-post.php, the REST API, pingbacks, and theX-Pingbackheader. - Existing comments:
comments_arrayreturns an empty list to classic themes, andget_comments_numberreports zero so themes and the Comments block stop showing counts and threads. - Widgets and blocks: The Latest Comments block renders nothing, and the Recent Comments widget is unregistered.
- Feeds: The comments feed link is removed from the page head, and requests for any comment feed redirect to the home page.
- Admin: The Comments menu is removed, direct visits to
edit-comments.phpredirect to the dashboard withwp_safe_redirect(), and the comment bubble is removed from the admin bar. Core adds that node at priority 60, so the removal runs at 100. - XML-RPC: Removes the
pingback.pingandpingback.extensions.getPingbacksmethods. The often-quotedxmlrpc_enabledfilter does not do this. The core documentation for that filter says it only controls methods that require authentication, not pingbacks. - REST API: Requests to
/wp/v2/commentsreturn a 403 error unless the user can edit posts. I did not remove the routes because WordPress 6.9 and later store block editor Notes as comments and use the same route. Removing it breaks Notes for your editors.
What changed from my 2023 code: support removal now runs on init instead of admin_init, so it also applies on the front end and in the REST API. I dropped the add_theme_support( 'automatic-feed-links' ) line, which turned on a theme feature as a side effect. I switched to wp_safe_redirect(), and I added the admin bar, comment feeds, XML-RPC pingbacks, REST access, and block theme output, which the old code missed.
The dashboard’s At a Glance and Activity widgets still link to comments that exist in the database. They go away once you delete those comments.
Disable Comments on Specific Post Types OnlyIf you want comments on posts but not on pages or media, list the post types to close and leave the rest alone:
<?php
/**
* Plugin Name: Disable Comments by Post Type
* Description: Closes comments and pings only for the post types listed below.
*/
defined( 'ABSPATH' ) || exit;
function mysite_no_comment_post_types() {
return array( 'page', 'attachment' );
}
add_action(
'init',
function () {
foreach ( mysite_no_comment_post_types() as $post_type ) {
remove_post_type_support( $post_type, 'comments' );
remove_post_type_support( $post_type, 'trackbacks' );
}
},
100
);
function mysite_close_comments_by_type( $open, $post_id ) {
if ( in_array( get_post_type( $post_id ), mysite_no_comment_post_types(), true ) ) {
return false;
}
return $open;
}
add_filter( 'comments_open', 'mysite_close_comments_by_type', 20, 2 );
add_filter( 'pings_open', 'mysite_close_comments_by_type', 20, 2 );
If you run WooCommerce, remember that product reviews are comments on the product post type. Leave 'product' out of the list if you want to keep reviews.
Classic themes print comments through comments_template() and the theme’s comments.php file, which usually checks have_comments() and comments_open(). The comments_array and comments_open filters in the plugin handle that path.
Block themes place a Comments block in the Single Posts template. In WordPress 7.1.4, that block outputs nothing when comments are closed and the post’s comment count is zero, which is why the plugin filters get_comments_number as well. You can also remove the block itself: go to Appearance > Editor > Templates, open Single Posts and any other templates that include it, select the Comments block, and delete it.
Back up the database first. Then count and delete regular comments, pingbacks, and trackbacks with WP-CLI. The --status=any flag covers approved, pending, spam, and trashed comments. The --type__in filter leaves Notes and other comment types, such as WooCommerce reviews and order notes, untouched.
wp db export before-deleting-comments.sql
wp comment list --status=any --type__in=comment,pingback,trackback --format=count
wp comment list --status=any --type__in=comment,pingback,trackback --format=ids | xargs -n 500 wp comment delete --force
Deleting through WordPress also updates each post’s comment count. If you prefer a single SQL statement in phpMyAdmin or the MySQL client, this is what I originally ran. It removes everything in the comments tables, including Notes, product reviews, and order notes, so only use it on a site that has none of those. Change the wp_ prefix if your tables use a different one.
TRUNCATE TABLE wp_commentmeta;
TRUNCATE TABLE wp_comments;
UPDATE wp_posts SET comment_count = 0;
The last line matters. TRUNCATE does not touch the comment count stored on each post, so themes can keep showing old totals without it.
If you are not comfortable maintaining code, manage a site with several administrators, or want checkboxes for each post type, a disable-comments plugin from the WordPress plugin directory does the same job. Pick one that is actively maintained and tested with your version of WordPress, and do not run it alongside this code. Two systems doing the same thing makes troubleshooting harder. A plugin does not close the registration hole, though. That setting is still yours to turn off.
How I Tested This CodeI ran every PHP sample through php -l and loaded each one as a must-use plugin on a clean WordPress 7.1.4 install with posts, pages, approved, pending, spam, and trashed comments, pingbacks, trackbacks, a review, and a Note. I compared every hook name and signature with the WordPress 7.1.4 source. With the plugin active:
- Comments and pings reported closed on old posts that were still open in the database, and the post types no longer supported comments.
- The Comments block, Post Comments Form block, and Latest Comments block rendered nothing, and a classic
comments_template()call returned no comments. - Comment feed requests returned a 301 redirect to the home page while the main feed still loaded.
- Logged-out REST requests to
/wp/v2/commentsreturned 403, an administrator could still read Notes, and a new REST comment from an administrator was rejected because comments were closed. - The Comments menu and admin bar node were gone,
edit-comments.phpredirected to the dashboard, and the pingback methods were removed from XML-RPC. - A registration with a sentence and domain name as the username was rejected with the hardening plugin and accepted without it, and the default admin email then showed that username in plain text.
- Every WP-CLI command above ran as written. The delete command removed comments, pingbacks, and trackbacks and left the Note and review in place.
No. That setting only applies to new posts. Close existing posts with Bulk Edit, WP-CLI, or a direct database update, or override them in code with the comments_open filter.
No. Closing comments and hiding them leaves them in the database. Delete them separately after taking a backup if you want them gone.
Only if you need public accounts. On a single site, WordPress accepts usernames with spaces, periods, and domain names, and spammers are using that field to send phishing text through registration emails. If you disabled comments, turn registration off.
No. The plugin keeps the comments REST route available to users who can edit posts, and the delete commands skip the note comment type.
A must-use plugin keeps working when you change themes and cannot be deactivated by accident. A child theme functions.php file also works if you never switch themes.
©2026 DK New Media, LLC, All rights reserved | Disclosure
Originally Published on Martech Zone: WordPress: Why I Removed Comments and How to Disable Them Site-Wide