Description
Security Hardener applies WordPress security best practices based on the WordPress Advanced Administration / Security / Hardening documentation and widely accepted hardening measures. It uses WordPress core functions and follows best practices without modifying core files.
Key Features
File Security:
* Disable file editor in WordPress admin
* Optionally disable all file modifications
XML-RPC Protection:
* Disable XML-RPC completely
* Remove pingback methods when XML-RPC is enabled
Pingback Protection:
* Disable self-pingbacks
* Remove X-Pingback header
* Block incoming pingbacks
User Enumeration Protection:
* Block /?author=N queries (returns 404)
* Secure REST API user endpoints (require authentication)
* Remove users from XML sitemaps
* Prevent canonical redirects that expose usernames
* Optionally block author feed pages (/author/username/feed/)
* Optionally anonymize the author name in oEmbed responses
Login Security:
* Generic error messages (no username/password hints)
* Login honeypot
* Block unsafe usernames
* Application Passwords disabled by default
* IP-based rate limiting with configurable thresholds
* Automatic blocking after failed attempts
Security Headers:
* X-Frame-Options: SAMEORIGIN (clickjacking protection)
* X-Content-Type-Options: nosniff (MIME sniffing protection)
* Referrer-Policy: strict-origin-when-cross-origin
* Permissions-Policy (restricts geolocation, microphone, camera)
* Optional HSTS (HTTP Strict Transport Security) for HTTPS sites — max-age set to 1 year
Additional Hardening:
* Hide WordPress version (meta generator tag and asset query strings)
* Remove obsolete wp_head items (RSD, WLW manifest, shortlink, emoji scripts)
* Security event logging (last 100 events)
* System Status — monitors file permissions, WP_DEBUG, user registration, PHP version, administrator accounts, and database version
⚠️ Important: Always test security settings in a staging environment first. Some features may affect third-party integrations or plugins.
Privacy: This plugin does not send data to external services and does not create custom database tables. It stores plugin settings and a security event log in the WordPress options table, and uses transients for temporary login attempt tracking. All data is preserved on uninstall by default and only deleted if the « Delete all data on uninstall » option is explicitly enabled.
Installation
Automatic Installation
- Go to Plugins > Add New Plugin
- Search for Security Hardener
- Click Install Now and then Activate
- Configure settings at Settings > Security Hardener
FAQ
-
What are the default settings?
-
By default, the plugin enables:
* File editor disabled
* XML-RPC disabled
* User enumeration blocking
* Generic login errors
* Login honeypot
* Block unsafe usernames
* Login rate limiting (5 attempts per 15 minutes)
* Security headers
* WordPress version hiding (meta generator tag and asset query strings)
* Clean wp_head output
* Security event logging
* Application Passwords disabledHSTS and author feed blocking are disabled by default. Application Passwords are disabled by default — re-enable this option only if you use the WordPress mobile app, Jetpack, or other REST API integrations that require them.
-
Does this plugin slow down my site?
-
No. The plugin uses lightweight WordPress hooks and native functions. Security headers add negligible overhead, and rate limiting only checks transients during login attempts.
-
I use a CDN or proxy (Cloudflare, etc.). How do I get the correct IP?
-
By default, rate limiting uses
REMOTE_ADDR. If behind a trusted proxy, add this towp-config.php:define('WPSH_TRUSTED_PROXIES', array( '173.245.48.0', // Example: Cloudflare IP range // Add your proxy IPs here ));The plugin will then check
HTTP_CF_CONNECTING_IP(Cloudflare) orHTTP_X_FORWARDED_FORheaders. -
What headers does this plugin add?
-
When security headers are enabled:
*X-Frame-Options: SAMEORIGIN
*X-Content-Type-Options: nosniff
*Referrer-Policy: strict-origin-when-cross-origin
*Permissions-Policy: geolocation=(), microphone=(), camera=()When HSTS is enabled (HTTPS only):
*Strict-Transport-Security: max-age=31536000(optionally withincludeSubDomainsif enabled) -
Does the plugin work with page caching?
-
Yes. Security headers are sent at the PHP level before caching. However, if you use aggressive server-level caching, you may need to configure your cache to allow these headers through.
-
Can I use this with other security plugins?
-
Yes, but be careful of conflicts. If another plugin also:
* Sends security headers, you may get duplicates (usually harmless)
* Blocks user enumeration, one should be disabled
* Has login rate limiting, choose one to avoid confusionThis plugin is designed to be lightweight and focused on core WordPress hardening.
-
What happens to my data when I uninstall?
-
When you uninstall (not just deactivate) the plugin, data is preserved by default. If you have enabled the « Delete all data on uninstall » option under Settings > Security Hardener > Other Settings, then on uninstall:
* All plugin settings are deleted
* All security logs are deleted
* All login rate limiting transients are cleared
* Your WordPress installation is returned to its default stateNote: Deactivating the plugin always preserves all settings.
-
Does this block the WordPress REST API?
-
No. The plugin only secures user-related endpoints by requiring authentication. All other REST API functionality works normally. Public endpoints like oEmbed continue to work.
-
I’m locked out after too many failed attempts. What do I do?
-
Failed login blocks expire automatically based on your configured window (default: 15 minutes). Wait for the block period to expire, or:
- Access your database (phpMyAdmin, etc.)
- Search for options with
_transient_wpsh_login_in the name - Delete those transient options
- Try logging in again
-
How do I know if the plugin is working?
-
- Check Settings > Security Hardener for active features
- Review the « Recent Security Events » log
- Use browser dev tools to inspect HTTP headers
- Try accessing
/?author=1(should return 404 if blocking is enabled) - Test failed login attempts to verify rate limiting
-
Does this plugin require HTTPS?
-
Not required, but strongly recommended. HSTS features require HTTPS. For maximum security, your entire site should use HTTPS with a valid SSL certificate.
-
Is this plugin compatible with multisite?
-
The plugin is designed for single-site installations. Multisite compatibility has not been tested and is not officially supported at this time.
Avis
Il n’y a aucun avis pour cette extension.
Contributeurs/contributrices & développeurs/développeuses
« Security Hardener » est un logiciel libre. Les personnes suivantes ont contribué à cette extension.
Contributeurs“Security Hardener” a été traduit dans 3 locales. Remerciez l’équipe de traduction pour ses contributions.
Traduisez « Security Hardener » dans votre langue.
Le développement vous intéresse ?
Parcourir le code, consulter le SVN dépôt, ou s’inscrire au journal de développement par RSS.
Journal des modifications
2.4.2 – 2026-04-24
- Improved: Added RTL language support.
- Updated: Some text improvements.
2.4.1 – 2026-04-18
- Improved: System Status PHP version check now uses wp_check_php_version().
- Improved: System Status database version check now reads directly from wpdb.
- Improved: System Status administrator count now uses WP_User_Query with count_total.
- Improved: System Status PHP version and database version status messages are now consistent.
- Improved: System Status PHP and database checks now show yellow when below the recommended version, green when above.
- Fixed: Type hints added to get_option(), sanitize_options(), remove_xmlrpc_pingback(), remove_x_pingback(), and disable_self_pingbacks() for PHP 8.2+ consistency.
- Fixed: render_system_status() docblock updated to reflect all six checks.
- Fixed: WPSH_FILE constant removed — activation hooks now use FILE directly.
2.4.0 – 2026-04-18
- Improved: Settings page reduced from 7 to 6 cards — HSTS options merged into Security headers card
- Improved: « Disable Application Passwords » moved to Login security card, between Block unsafe usernames and Login rate limiting
- Improved: System Status section now appears before Recent Security Events
- Improved: System Status expanded with four new checks — public user registration status, PHP version, number of administrator accounts, and database version
- Improved: System Status is now collapsible
- Improved: Hardening checklist now displays in two columns to reduce vertical space
- Improved: « Reset all » renamed to « Reset recommendations » and styled as a standard button for consistency with Clear Logs
- Improved: Removed persistent « Important » notice from the settings page header
- Improved: Added aria-label to all toggle inputs for screen reader accessibility
- Improved: Checklist items shortened to fit a single line in two-column layout
- Added: Four new checklist items — disable directory browsing, use SFTP instead of FTP, install plugins/themes from trusted sources only, keep active plugins minimal
- Removed: Two redundant checklist items — « Rename the default admin account » and « Disable WP_DEBUG on live sites »
- Fixed: AJAX checklist validation aligned with actual item count throughout
2.3.1 – 2026-04-17
- Fixed: Users with an existing unsafe username (e.g. admin) can now update their profile without being blocked — the restriction applies only to new user registration.
2.3.0 – 2026-04-17
- Added: Block unsafe usernames — prevents registration using commonly targeted usernames such as admin, root, test, or webmaster.
- Added: Anonymize oEmbed author — replaces the author name in oEmbed responses with the site name to prevent username exposure.
- Added: Two new items to the hardening checklist — remove wp-config.php backup files and remove publicly accessible database exports.
- Fixed: Honeypot POST check now uses sanitize_text_field() and wp_unslash(), resolving a PHPCS warning.
- Fixed: Duplicate wp_create_nonce() call removed from render_checklist().
- Fixed: block_author_feeds description updated to clarify it will break existing subscriptions, not merely affect them.
- Fixed: System Status table header corrected from « Check » to « Checks ».
- Fixed: log_security_events fallback value normalized from true to 1 for consistency with the options array.
2.2.0 – 2026-04-01
- Added: Login honeypot — a hidden field added to the login form that silently blocks bots before any credential check.
- Added: Block author feeds — optionally blocks /author/username/feed/ pages that can confirm existing usernames.
- Added: Disable Application Passwords — disables REST API authentication via Application Passwords; enabled by default.
- Added: System Status section — replaces the separate File Permissions section; shows file permissions and WP_DEBUG status in a unified table, always visible with color-coded indicators.
- Added: Two new items to the hardening checklist — disable display_errors in PHP configuration, and disable WP_DEBUG_DISPLAY on live sites.
2.1.1 – 2026-03-29
- Improved: Interactive elements (toggles, checklist, progress bar) now use WordPress admin theme color variables.
- Fixed: « Settings saved. » notice no longer appears twice after saving settings.
- Fixed: Checklist progress counter now stays translated when updated via AJAX.
- Fixed: Removed orphan comment from show_admin_notices().
- Fixed: Removed dead CSS for .wpsh-recommendations.
2.1.0 – 2026-03-26
- Added: Interactive hardening checklist — users can mark each recommendation as done.
- Improved: File permissions check moved from floating admin notice into the settings page.
- Fixed: « Enable preload » description now clarifies it only adds the preload directive to the HSTS header.
- Fixed: « Clean wp_head » description no longer uses HTML entities that rendered as literal text in some contexts.
- Fixed: wp-includes recommendation text no longer implies a code snippet follows.
2.0.2 – 2026-03-21
- Improved: Number fields now show allowed range as permanent help text (Min/Max) below the label.
- Fixed: Lost password flow now uses lostpassword_errors filter instead of login_messages.
- Fixed: « Clear Logs » replaced JavaScript confirm() with a proper POST form.
- Fixed: $_POST[‘wpsh_action’] sanitized with sanitize_key() before comparison, following WordPress Coding Standards.
2.0.1 – 2026-03-21
- Fixed: « Block user enumeration » description now mentions canonical redirect blocking
- Fixed: « Login rate limiting » toggle and number fields now have descriptions for consistency with all other options
- Fixed: Removed unused WPSH_DIR and WPSH_URL constants
- Fixed: Removed misleading inline comment about file editing in init()
- Fixed: readme — « Disable self-pingbacks » moved from XML-RPC section to its own Pingback Protection section
- Fixed: Unused parameters in clear_login_attempts() prefixed with underscore following WordPress Coding Standards
- Fixed: @return docblock for get_default_options() updated to array<string, int> for accuracy
- Fixed: Activation and deactivation log messages now pass through __() for translation
- Fixed: remove_login_hints() no longer relies on hardcoded English string comparison
- Fixed: strpos() replaced with str_starts_with() in disable_self_pingbacks() — consistent with PHP 8.2+ usage throughout the plugin
- Fixed: Explicit string types added to log_security_event() method signature
- Fixed: @param docblock of clear_login_attempts() updated to reflect renamed parameters $_user_login and $_user
2.0.0 – 2026-03-20
- Improved: Complete redesign of the settings page — responsive 3-column card grid replaces the default WordPress Settings API table layout
- Improved: Checkboxes replaced with CSS toggles for clearer on/off state at a glance
- Improved: « Clear Logs » button moved inline next to the section heading
- Improved: Minimal inline CSS scoped to .wpsh-* classes — no external stylesheet enqueued
- Removed: « Enable security headers » master toggle — each header is now controlled individually
- Removed: Configurable HSTS max-age field — hardcoded to 31536000 seconds (1 year), the universally recommended value
- Fixed: HSTS « Include subdomains » now defaults to disabled — users must opt in explicitly
- Improved: « Hide WordPress version » now also strips the WordPress version from script and style asset URLs (?ver=), preventing version detection via asset URLs
- Improved: « Hide WordPress version » moved from « User Enumeration » to « Other Settings »
- Improved: « Clean wp_head » no longer removes feed links extra — avoids conflicts with plugins that rely on category and tag feeds
- Improved: « Clean wp_head » no longer removes wp_generator — already covered by « Hide WordPress version »
- Improved: All toggle descriptions updated for consistency — each option now explains what value or behaviour it applies
- Added: New « Additional Hardening Recommendations » section in the plugin admin page and readme, including four measures from the official WordPress Hardening Guide not previously listed: rename admin account, restrict database user privileges, protect wp-config.php, and block direct access to wp-includes/
1.0 – 2026-03-05
- Updated: Minimum WordPress requirement raised to 6.9
- Updated: Minimum PHP requirement raised to 8.2
- Improved: Applied PHP 8.3 modern syntax throughout — typed class properties, explicit return types on all methods, short array syntax, and
matchexpression insanitize_options() - Added:
delete_data_on_uninstalloption (default: disabled) — users must explicitly opt in to data deletion on uninstall; data is preserved by default - Fixed:
uninstall.phpno longer uses direct SQL queries; deletion is now conditional on the opt-in option and uses WordPress APIs exclusively - Fixed:
WPSH_VERSIONconstant kept in sync with plugin header at1.0
0.8 – 2026-02-26
- Improved: Moved define_security_constants() from plugins_loaded hook to the constructor, ensuring DISALLOW_FILE_EDIT and DISALLOW_FILE_MODS are defined as early as possible in the WordPress lifecycle
- Improved: Expanded @param docblock for render_checkbox_field() to document all $args keys
- Added: WordPress Playground blueprint (assets/blueprints/blueprint.json) enabling live plugin preview directly from the WordPress.org plugin directory
- Fixed: Plugin header description updated to remove REST API restriction option removed in 0.5
- Fixed: Removed stale phpcs:ignore comment in show_admin_notices() — nonce verification is now correctly documented inline
- Fixed: Wrapped login block error message with wp_kses_post() for consistent output escaping
- Fixed: Added esc_url() and esc_html__() to add_settings_link() sprintf output
- Fixed: Removed redundant get_client_ip() call in log_security_event() — IP resolved once per event
- Fixed: Added autoload=false to wpsh_security_logs option — logs are only needed on the settings page, not loaded on every request
0.7 – 2026-02-21
- Fixed: WPSH_VERSION constant updated to match plugin header version
- Fixed: Added wp_unslash() and sanitize_text_field() to $_GET[‘author’] in prevent_author_redirect()
- Fixed: Moved HTML markup outside translatable strings in generic_login_errors(), check_login_rate_limit(), and field descriptions for « Disable all file modifications » and « Enable HSTS »
- Security: Added CSRF protection to « Clear Logs » action via wp_nonce_url() and wp_verify_nonce()
- Improved: Added missing hardening recommendations to admin page: BasicAuth protection for wp-admin and changing the default database table prefix
- Fixed: Corrected date format in changelog entries (YYYY-MM-DD)
0.6 – 2026-02-21
- Fixed: Removed deprecated load_plugin_textdomain() call (automatic since WordPress 4.6)
- Fixed: Added wp_unslash() and sanitize_text_field() to $_GET[‘author’] before sanitization
- Fixed: Moved HTML markup outside translatable string in login confirmation message
- Fixed: Escaped $min and $max output in render_number_field() using absint()
- Fixed: Added phpcs:ignore for native WordPress constants DISALLOW_FILE_EDIT and DISALLOW_FILE_MODS
- Fixed: Removed error_log() debug call from uninstall.php
- Fixed: Suppressed false-positive direct database query warning in uninstall.php with inline justification comment
- Fixed: Removed redundant function_exists() check for wp_cache_flush() in uninstall.php
0.5 – 2026-02-09
- Complete rewrite following WordPress hardening best practices
- Increased minimum PHP requirement to 8.0 (PHP 7.4 is end-of-life)
- Added: Security event logging system (last 100 events)
- Added: File permission checking with admin notices
- Improved: User enumeration blocking (now also blocks REST endpoints and sitemaps)
- Improved: Rate limiting algorithm (more reliable, fewer race conditions)
- Improved: IP detection with proper proxy support via
WPSH_TRUSTED_PROXIESconstant - Improved: Admin interface with better organization and descriptions
- Improved: Code quality following WordPress Coding Standards
- Removed: CSP (Content Security Policy) – requires per-site customization
- Removed: REST API restriction option – too broad, better handled per-case
- Fixed: All security vulnerabilities from previous versions
- Fixed: Proper sanitization and escaping throughout
0.3 – 2025-10-20
- Some corrections
0.2 – 2025-10-13
- Some corrections
0.1 – 2025-10-04
- Initial release