Skip to content

Instantly share code, notes, and snippets.

@coulterpeterson
Created August 26, 2025 02:03
Show Gist options
  • Save coulterpeterson/bb623c1edc1e74f91aaf18a79cb85955 to your computer and use it in GitHub Desktop.
Save coulterpeterson/bb623c1edc1e74f91aaf18a79cb85955 to your computer and use it in GitHub Desktop.
WordPress Security Hardening MU Plugin (Disable Comments & More)
<?php
/**
* Plugin Name: Security Hardening & Disable Comments
* Description: Completely disables WordPress comments and implements security best practices
* Version: 2.0.0
* Author: Maplespace
*
* This is a Must-Use plugin. Place this file in /wp-content/mu-plugins/
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
/**
* Main plugin class for security hardening and comment removal
*/
class WPSecurityHardening {
/**
* Initialize all plugin features
*/
public static function init() {
// Initialize comment disabling
self::init_disable_comments();
// Initialize security hardening
self::init_security_hardening();
// Add admin interface
add_action('admin_menu', [__CLASS__, 'add_admin_menu']);
add_action('admin_init', [__CLASS__, 'register_settings']);
// Handle AJAX comment deletion
add_action('wp_ajax_delete_all_comments_action', [__CLASS__, 'ajax_delete_all_comments']);
// Add admin scripts
add_action('admin_enqueue_scripts', [__CLASS__, 'enqueue_admin_scripts']);
}
/**
* Initialize comment disabling features
*/
private static function init_disable_comments() {
// Admin-specific actions
add_action('admin_init', [__CLASS__, 'admin_init']);
add_action('admin_menu', [__CLASS__, 'remove_comment_menus'], 999);
// Frontend comment blocking
add_filter('comments_open', '__return_false', 20, 2);
add_filter('pings_open', '__return_false', 20, 2);
add_filter('comments_array', '__return_empty_array', 10, 2);
// Admin bar cleanup
add_action('init', [__CLASS__, 'remove_admin_bar_comments']);
add_action('wp_before_admin_bar_render', [__CLASS__, 'remove_admin_bar_comments_css']);
// Remove comment feeds
add_filter('wp_headers', [__CLASS__, 'remove_x_pingback']);
add_action('template_redirect', [__CLASS__, 'remove_comment_feed']);
// Remove comment widgets
add_action('widgets_init', [__CLASS__, 'remove_comment_widgets']);
// Remove comments from REST API
add_filter('rest_endpoints', [__CLASS__, 'remove_comment_endpoints']);
// Remove comment support from post types
add_action('init', [__CLASS__, 'remove_comment_support'], 100);
}
/**
* Initialize security hardening features
*/
private static function init_security_hardening() {
// Hide WordPress version
remove_action('wp_head', 'wp_generator');
add_filter('the_generator', '__return_empty_string');
add_filter('style_loader_src', [__CLASS__, 'remove_version_strings'], 9999);
add_filter('script_loader_src', [__CLASS__, 'remove_version_strings'], 9999);
// Remove various WordPress headers
remove_action('wp_head', 'rsd_link');
remove_action('wp_head', 'wlwmanifest_link');
remove_action('wp_head', 'wp_shortlink_wp_head');
remove_action('wp_head', 'rest_output_link_wp_head');
remove_action('wp_head', 'wp_oembed_add_discovery_links');
remove_action('template_redirect', 'rest_output_link_header', 11);
// Remove emoji support (performance improvement)
remove_action('wp_head', 'print_emoji_detection_script', 7);
remove_action('wp_print_styles', 'print_emoji_styles');
remove_action('admin_print_scripts', 'print_emoji_detection_script');
remove_action('admin_print_styles', 'print_emoji_styles');
add_filter('emoji_svg_url', '__return_false');
// Disable XML-RPC
add_filter('xmlrpc_enabled', '__return_false');
add_filter('pings_open', '__return_false');
// Remove DNS prefetch
remove_action('wp_head', 'wp_resource_hints', 2);
// Security headers
add_action('send_headers', [__CLASS__, 'add_security_headers']);
// Hide login errors (prevents username enumeration)
add_filter('login_errors', function() {
return __('Login details are incorrect.');
});
// Disable file editing in admin
if (!defined('DISALLOW_FILE_EDIT')) {
define('DISALLOW_FILE_EDIT', true);
}
// Remove unnecessary dashboard widgets
add_action('wp_dashboard_setup', [__CLASS__, 'remove_dashboard_widgets']);
// Disable author archives to prevent user enumeration
add_action('template_redirect', [__CLASS__, 'disable_author_archives']);
// Remove user enumeration via REST API
add_filter('rest_endpoints', [__CLASS__, 'restrict_user_endpoints']);
}
/**
* Admin initialization for comment disabling
*/
public static function admin_init() {
// Redirect from comments page (but not if we're on our settings page)
global $pagenow;
$current_page = isset($_GET['page']) ? $_GET['page'] : '';
if ($pagenow === 'edit-comments.php') {
wp_safe_redirect(admin_url());
exit;
}
// Only redirect from discussion page if it's not our custom page
if ($pagenow === 'options-discussion.php' && $current_page !== 'security-hardening') {
wp_safe_redirect(admin_url('options-general.php?page=security-hardening'));
exit;
}
// Remove comments metabox from dashboard
remove_meta_box('dashboard_recent_comments', 'dashboard', 'normal');
// Remove comments column from post/page lists
add_filter('manage_posts_columns', [__CLASS__, 'remove_comments_column']);
add_filter('manage_pages_columns', [__CLASS__, 'remove_comments_column']);
// Remove Quick Edit comment options
add_filter('bulk_actions-edit-post', [__CLASS__, 'remove_bulk_comments']);
add_filter('bulk_actions-edit-page', [__CLASS__, 'remove_bulk_comments']);
}
/**
* Remove comment support from all post types
*/
public static function remove_comment_support() {
foreach (get_post_types() as $post_type) {
if (post_type_supports($post_type, 'comments')) {
remove_post_type_support($post_type, 'comments');
remove_post_type_support($post_type, 'trackbacks');
}
}
}
/**
* Add admin menu for settings
*/
public static function add_admin_menu() {
add_options_page(
'Security & Comments Settings',
'Security Hardening',
'manage_options',
'security-hardening',
[__CLASS__, 'admin_page']
);
}
/**
* Register plugin settings
*/
public static function register_settings() {
register_setting('security_hardening_settings', 'security_hardening_options');
}
/**
* Admin page HTML
*/
public static function admin_page() {
if (!current_user_can('manage_options')) {
return;
}
// Get comment statistics
global $wpdb;
$comment_count = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->comments}");
$spam_count = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->comments} WHERE comment_approved = 'spam'");
$trash_count = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->comments} WHERE comment_approved = 'trash'");
$pending_count = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->comments} WHERE comment_approved = '0'");
?>
<div class="wrap">
<h1><?php echo esc_html(get_admin_page_title()); ?></h1>
<div class="notice notice-info">
<p><strong>Active Features:</strong></p>
<ul style="list-style: disc; margin-left: 20px;">
<li>✓ Comments completely disabled</li>
<li>✓ WordPress version hidden</li>
<li>✓ XML-RPC disabled</li>
<li>✓ Security headers added</li>
<li>✓ Login error messages obscured</li>
<li>✓ File editing disabled</li>
<li>✓ User enumeration blocked</li>
<li>✓ Unnecessary headers removed</li>
</ul>
</div>
<div class="card" style="max-width: 600px; margin-top: 20px;">
<h2>Comment Management</h2>
<p>Comments are currently <strong style="color: red;">DISABLED</strong> site-wide.</p>
<h3>Comment Statistics</h3>
<table class="widefat" style="max-width: 400px;">
<tbody>
<tr>
<td><strong>Total Comments:</strong></td>
<td><?php echo number_format($comment_count); ?></td>
</tr>
<tr>
<td><strong>Spam Comments:</strong></td>
<td><?php echo number_format($spam_count); ?></td>
</tr>
<tr>
<td><strong>Trashed Comments:</strong></td>
<td><?php echo number_format($trash_count); ?></td>
</tr>
<tr>
<td><strong>Pending Comments:</strong></td>
<td><?php echo number_format($pending_count); ?></td>
</tr>
</tbody>
</table>
<?php if ($comment_count > 0): ?>
<div style="margin-top: 20px; padding: 15px; background: #fff; border: 1px solid #c3c4c7; border-left: 4px solid #d63638;">
<h3 style="margin-top: 0; color: #d63638;">⚠️ Delete All Comments</h3>
<p><strong>Warning:</strong> This action will permanently delete ALL comments from your database. This cannot be undone!</p>
<p>
<label>
<input type="checkbox" id="confirm-delete-comments" />
I understand this will permanently delete <?php echo number_format($comment_count); ?> comment(s)
</label>
</p>
<button type="button"
id="delete-all-comments-btn"
class="button button-primary"
disabled
style="background: #d63638; border-color: #d63638;">
Delete All Comments Permanently
</button>
<div id="delete-comments-result" style="margin-top: 15px;"></div>
</div>
<?php else: ?>
<div style="margin-top: 20px; padding: 15px; background: #f0f8f0; border: 1px solid #c3c4c7; border-left: 4px solid #00a32a;">
<p style="margin: 0;">✓ No comments found in the database.</p>
</div>
<?php endif; ?>
</div>
<div class="card" style="max-width: 600px; margin-top: 20px;">
<h2>Security Status</h2>
<p>The following security enhancements are active:</p>
<ul style="list-style: disc; margin-left: 20px;">
<li>WordPress version information removed from HTML, RSS feeds, scripts, and styles</li>
<li>XML-RPC completely disabled (prevents brute force attacks)</li>
<li>Login error messages unified (prevents username enumeration)</li>
<li>File editing disabled in WordPress admin</li>
<li>Author archives disabled (prevents user enumeration)</li>
<li>User REST API endpoints restricted</li>
<li>Security headers added (X-Content-Type-Options, X-Frame-Options, etc.)</li>
<li>Unnecessary WordPress headers and features removed</li>
</ul>
</div>
</div>
<?php
}
/**
* Enqueue admin scripts
*/
public static function enqueue_admin_scripts($hook) {
if ($hook !== 'settings_page_security-hardening') {
return;
}
?>
<script type="text/javascript">
document.addEventListener('DOMContentLoaded', function() {
const checkbox = document.getElementById('confirm-delete-comments');
const deleteBtn = document.getElementById('delete-all-comments-btn');
const resultDiv = document.getElementById('delete-comments-result');
if (!checkbox || !deleteBtn) {
return;
}
// Handle checkbox change
checkbox.addEventListener('change', function() {
deleteBtn.disabled = !this.checked;
});
// Handle delete button click
deleteBtn.addEventListener('click', function() {
if (!confirm('Are you absolutely sure? This will permanently delete ALL comments!')) {
return;
}
// Disable button and update text
deleteBtn.disabled = true;
deleteBtn.textContent = 'Deleting...';
// Prepare form data
const formData = new FormData();
formData.append('action', 'delete_all_comments_action');
formData.append('nonce', '<?php echo wp_create_nonce('delete_all_comments_nonce'); ?>');
// Make AJAX request
fetch(ajaxurl, {
method: 'POST',
credentials: 'same-origin',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
resultDiv.innerHTML = '<div class="notice notice-success"><p>' + data.data.message + '</p></div>';
deleteBtn.textContent = 'Deleted Successfully';
setTimeout(function() {
location.reload();
}, 2000);
} else {
resultDiv.innerHTML = '<div class="notice notice-error"><p>Error: ' + data.data.message + '</p></div>';
deleteBtn.disabled = false;
deleteBtn.textContent = 'Delete All Comments Permanently';
checkbox.checked = false;
}
})
.catch(error => {
console.error('Error:', error);
resultDiv.innerHTML = '<div class="notice notice-error"><p>An error occurred. Please try again.</p></div>';
deleteBtn.disabled = false;
deleteBtn.textContent = 'Delete All Comments Permanently';
checkbox.checked = false;
});
});
});
</script>
<?php
}
/**
* AJAX handler for deleting all comments
*/
public static function ajax_delete_all_comments() {
// Check nonce and capabilities
if (!wp_verify_nonce($_POST['nonce'], 'delete_all_comments_nonce') || !current_user_can('manage_options')) {
wp_send_json_error(['message' => 'Security check failed']);
}
global $wpdb;
// Get count before deletion
$count = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->comments}");
// Delete all comments
$wpdb->query("DELETE FROM {$wpdb->comments}");
// Delete all comment meta
$wpdb->query("DELETE FROM {$wpdb->commentmeta}");
// Reset comment counts for all posts
$wpdb->query("UPDATE {$wpdb->posts} SET comment_count = 0");
wp_send_json_success([
'message' => sprintf('Successfully deleted %s comment(s) from the database.', number_format($count))
]);
}
/**
* Remove comment menus
*/
public static function remove_comment_menus() {
remove_menu_page('edit-comments.php');
// Remove Discussion from Settings menu, but only if it's not our page
global $submenu;
if (isset($submenu['options-general.php'])) {
foreach ($submenu['options-general.php'] as $index => $menu_item) {
if ($menu_item[2] === 'options-discussion.php') {
unset($submenu['options-general.php'][$index]);
}
}
}
}
/**
* Remove version strings from scripts and styles
*/
public static function remove_version_strings($src) {
if (strpos($src, '?ver=')) {
$src = remove_query_arg('ver', $src);
}
return $src;
}
/**
* Add security headers
*/
public static function add_security_headers() {
header('X-Content-Type-Options: nosniff');
header('X-Frame-Options: SAMEORIGIN');
header('X-XSS-Protection: 1; mode=block');
header('Referrer-Policy: strict-origin-when-cross-origin');
header('Permissions-Policy: geolocation=(), microphone=(), camera=()');
}
/**
* Remove dashboard widgets
*/
public static function remove_dashboard_widgets() {
remove_meta_box('dashboard_primary', 'dashboard', 'side'); // WordPress news
remove_meta_box('dashboard_secondary', 'dashboard', 'side'); // Other WordPress news
remove_meta_box('dashboard_quick_press', 'dashboard', 'side'); // Quick Press
remove_meta_box('dashboard_recent_drafts', 'dashboard', 'side'); // Recent Drafts
}
/**
* Disable author archives
*/
public static function disable_author_archives() {
if (is_author()) {
global $wp_query;
$wp_query->set_404();
status_header(404);
}
}
/**
* Restrict user endpoints in REST API
*/
public static function restrict_user_endpoints($endpoints) {
if (!is_user_logged_in() || !current_user_can('list_users')) {
if (isset($endpoints['/wp/v2/users'])) {
unset($endpoints['/wp/v2/users']);
}
if (isset($endpoints['/wp/v2/users/(?P<id>[\d]+)'])) {
unset($endpoints['/wp/v2/users/(?P<id>[\d]+)']);
}
}
return $endpoints;
}
/**
* Remove comments from admin bar
*/
public static function remove_admin_bar_comments() {
if (is_admin_bar_showing()) {
remove_action('admin_bar_menu', 'wp_admin_bar_comments_menu', 60);
}
}
/**
* Remove comments CSS from admin bar
*/
public static function remove_admin_bar_comments_css() {
global $wp_admin_bar;
$wp_admin_bar->remove_menu('comments');
}
/**
* Remove comments column from post listings
*/
public static function remove_comments_column($columns) {
unset($columns['comments']);
return $columns;
}
/**
* Remove bulk comment actions
*/
public static function remove_bulk_comments($actions) {
unset($actions['edit']);
return $actions;
}
/**
* Remove X-Pingback HTTP header
*/
public static function remove_x_pingback($headers) {
unset($headers['X-Pingback']);
return $headers;
}
/**
* Disable comment feeds
*/
public static function remove_comment_feed() {
if (is_comment_feed()) {
wp_die(__('Comments are disabled on this site.'), '', ['response' => 403]);
}
}
/**
* Remove comment widgets
*/
public static function remove_comment_widgets() {
unregister_widget('WP_Widget_Recent_Comments');
}
/**
* Remove comment endpoints from REST API
*/
public static function remove_comment_endpoints($endpoints) {
if (isset($endpoints['/wp/v2/comments'])) {
unset($endpoints['/wp/v2/comments']);
}
if (isset($endpoints['/wp/v2/comments/(?P<id>[\d]+)'])) {
unset($endpoints['/wp/v2/comments/(?P<id>[\d]+)']);
}
return $endpoints;
}
}
// Initialize the plugin
WPSecurityHardening::init();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment