{"id":396515,"date":"2026-11-05T22:29:31","date_gmt":"2026-11-05T22:29:31","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/hardenly\/"},"modified":"2026-11-06T04:18:50","modified_gmt":"2026-11-06T04:18:50","slug":"stoutwall","status":"publish","type":"plugin","link":"https:\/\/wordpress.org\/plugins\/stoutwall\/","author":12554924,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"1.3.0","stable_tag":"1.3.0","tested":"7.1.4","requires":"5.5","requires_php":"7.4","requires_plugins":null,"header_name":"Stoutwall \u2013 Security Hardening & Login Protection","header_author":"Felipe Pitol","header_description":"Turn on common security hardening measures with simple switches: block PHP in uploads, disable XML-RPC, stop user enumeration, hide the version and more.","assets_banners_color":"723de7","last_updated":"2026-11-06 04:18:50","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"https:\/\/wordpress.org\/plugins\/stoutwall\/","header_author_uri":"https:\/\/felipepitol.me","rating":0,"author_block_rating":0,"active_installs":0,"downloads":71,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"1.2.0":{"tag":"1.2.0","author":"felipepitol","date":"2026-11-05 22:29:18","revision":3786840},"1.2.1":{"tag":"1.2.1","author":"felipepitol","date":"2026-11-05 22:49:26","revision":3786857},"1.3.0":{"tag":"1.3.0","author":"felipepitol","date":"2026-11-06 04:18:50","revision":3787091}},"upgrade_notice":{"1.3.0":"<p>Adds login attempt limits and security headers (both on after updating), server rules for Nginx and OpenLiteSpeed, a security audit and WP-CLI commands.<\/p>","1.2.1":"<p>Updates the plugin link. No functional changes.<\/p>","1.2.0":"<p>Fixes uploads protection on sites that already had an .htaccess file in uploads, and fully disables XML-RPC pingbacks. Recommended for all users.<\/p>"},"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3786847,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3786847,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3786847,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3786847,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["1.2.0","1.2.1","1.3.0"],"block_files":[],"assets_screenshots":{"screenshot-1.png":{"filename":"screenshot-1.png","revision":3786840,"resolution":"1","location":"assets","locale":"","width":1280,"height":900},"screenshot-2.png":{"filename":"screenshot-2.png","revision":3786840,"resolution":"2","location":"assets","locale":"","width":1280,"height":900}},"screenshots":{"1":"Protections grouped by area, with a meter that shows how many are on.","2":"The status check shows whether each protection is working."}},"plugin_section":[],"plugin_tags":[31093,9374,600,153786,6558],"plugin_category":[54],"plugin_contributors":[290976],"plugin_business_model":[],"class_list":["post-396515","plugin","type-plugin","status-publish","hentry","plugin_tags-hardening","plugin_tags-limit-login-attempts","plugin_tags-security","plugin_tags-security-headers","plugin_tags-xml-rpc","plugin_category-security-and-spam-protection","plugin_contributors-felipepitol","plugin_committers-felipepitol"],"banners":{"banner":"https:\/\/ps.w.org\/stoutwall\/assets\/banner-772x250.png?rev=3786847","banner_2x":"https:\/\/ps.w.org\/stoutwall\/assets\/banner-1544x500.png?rev=3786847","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/stoutwall\/assets\/icon-128x128.png?rev=3786847","icon_2x":"https:\/\/ps.w.org\/stoutwall\/assets\/icon-256x256.png?rev=3786847","generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/stoutwall\/assets\/screenshot-1.png?rev=3786840","caption":"Protections grouped by area, with a meter that shows how many are on."},{"src":"https:\/\/ps.w.org\/stoutwall\/assets\/screenshot-2.png?rev=3786840","caption":"The status check shows whether each protection is working."}],"raw_content":"<!--section=description-->\n<p>Stoutwall applies well-known hardening measures that most sites should have, each behind its own switch. There is no firewall, no scanner and no account to create: it changes how WordPress behaves, and you can see and undo every change. One small plugin instead of five single-purpose ones.<\/p>\n\n<p><strong>What it can do<\/strong><\/p>\n\n<ul>\n<li><strong>Limit login attempts<\/strong> \u2013 after 5 failed logins in 15 minutes, the IP address is locked out for 15 minutes. Repeated lockouts last longer, up to 24 hours. Works behind Cloudflare and other proxies, with a list of allowed IP addresses and ranges that are never locked out.<\/li>\n<li><strong>Security headers<\/strong> \u2013 sends <code>X-Content-Type-Options<\/code>, <code>X-Frame-Options<\/code> and <code>Referrer-Policy<\/code>, plus optional HSTS.<\/li>\n<li><strong>Prevent code execution in uploads<\/strong> \u2013 adds rules to <code>wp-content\/uploads\/.htaccess<\/code> that block PHP files, including <code>.phtml<\/code>, <code>.php5<\/code>, <code>.phar<\/code> and double extensions such as <code>image.php.jpg<\/code>. Rules from other plugins in the same file are kept.<\/li>\n<li><strong>Disable XML-RPC<\/strong> \u2013 turns off every XML-RPC method, including <code>pingback.ping<\/code>, which is often abused for DDoS amplification, and removes the <code>X-Pingback<\/code> header.<\/li>\n<li><strong>Prevent user enumeration<\/strong> \u2013 stops <code>?author=N<\/code> lookups and hides usernames from the REST API for visitors, the users sitemap and oEmbed responses. Authors and editors keep the author list in the block editor.<\/li>\n<li><strong>Generic login errors<\/strong> \u2013 the login form no longer tells attackers whether a username exists. Registration errors are still shown.<\/li>\n<li><strong>Disable application passwords<\/strong> and <strong>require login for the REST API<\/strong> (both off by default).<\/li>\n<li><strong>Disable file editors<\/strong> \u2013 removes the theme and plugin file editors from the dashboard.<\/li>\n<li><strong>Disable registration<\/strong> \u2013 keeps \"Anyone can register\" off.<\/li>\n<li><strong>Block the \"admin\" username<\/strong> for new registrations.<\/li>\n<li><strong>Hide username as display name<\/strong> \u2013 users cannot save a profile whose public display name is their login.<\/li>\n<li><strong>Hide the WordPress version<\/strong> from the page head and feeds.<\/li>\n<li><strong>Remove the X-Powered-By header<\/strong> that reveals the PHP version.<\/li>\n<li><strong>Disable directory browsing<\/strong> in the uploads folder.<\/li>\n<\/ul>\n\n<p><strong>Check that it really works<\/strong><\/p>\n\n<ul>\n<li>A <strong>Check status<\/strong> button confirms that each protection is in effect on your server, for example that the <code>.htaccess<\/code> rules are present and the headers reach the browser, not only that the switch is on.<\/li>\n<li>A read-only <strong>security audit<\/strong> looks for weak security keys, copies of <code>wp-config.php<\/code>, a public <code>debug.log<\/code>, an \"admin\" user, open registration with a powerful role and more. It also tests from outside whether PHP files in uploads run and whether folders can be listed. The quick checks also appear in <strong>Tools \u2192 Site Health<\/strong>.<\/li>\n<\/ul>\n\n<p><strong>Rules for your web server<\/strong><\/p>\n\n<p>Stoutwall generates matching rules for <strong>Apache, LiteSpeed, OpenLiteSpeed and Nginx<\/strong>, so the protections also cover static files and servers that ignore <code>.htaccess<\/code>. You review and paste them; nothing outside the uploads folder is changed for you.<\/p>\n\n<p><strong>For developers and agencies<\/strong><\/p>\n\n<ul>\n<li>WP-CLI: <code>wp stoutwall list|status|enable|disable|export|import|lockouts|unlock|allowlist|rules|audit<\/code>.<\/li>\n<li>Export the settings to a JSON file and import them on other sites.<\/li>\n<li>Filters such as <code>stoutwall_security_headers<\/code>, <code>stoutwall_public_rest_namespaces<\/code>, <code>stoutwall_login_max_failures<\/code> and <code>stoutwall_client_ip<\/code>.<\/li>\n<\/ul>\n\n<p><strong>Privacy<\/strong><\/p>\n\n<p>Stoutwall does not send anything to external servers, does not add cookies and does not send emails. To limit login attempts it keeps, on your own site, a hashed IP address of failed logins for 15 minutes and a partial IP address (such as 203.0.113.x) of locked-out visitors for up to a day.<\/p>\n\n<p><strong>Reporting security issues<\/strong><\/p>\n\n<p>Please report vulnerabilities privately through the <a href=\"https:\/\/www.wordfence.com\/threat-intel\/bug-bounty-program\/\">Wordfence<\/a> or <a href=\"https:\/\/patchstack.com\/database\/report\">Patchstack<\/a> programs, which coordinate the fix with the author before disclosure.<\/p>\n\n<!--section=installation-->\n<ol>\n<li>Install Stoutwall from <strong>Plugins \u2192 Add New<\/strong>, or upload the <code>stoutwall<\/code> folder to <code>\/wp-content\/plugins\/<\/code>.<\/li>\n<li>Activate the plugin. The protections that rarely cause problems start enabled.<\/li>\n<li>Go to <strong>Settings \u2192 Stoutwall<\/strong> to turn protections on or off, then click <strong>Check status<\/strong>.<\/li>\n<li>On Nginx or OpenLiteSpeed, open the <strong>Server rules<\/strong> tab and add the rules to your server.<\/li>\n<\/ol>\n\n<!--section=faq-->\n<dl>\n<dt id=\"does%20it%20work%20on%20nginx%20and%20openlitespeed%3F\"><h3>Does it work on Nginx and OpenLiteSpeed?<\/h3><\/dt>\n<dd><p>Most protections work on any server. Blocking PHP in uploads relies on <code>.htaccess<\/code>, which Apache and LiteSpeed Enterprise read but Nginx and OpenLiteSpeed ignore. Open <strong>Settings \u2192 Stoutwall \u2192 Server rules<\/strong>, pick your server and paste the rules it shows. Then run the audit: it tests whether PHP in uploads is really blocked.<\/p><\/dd>\n<dt id=\"i%20locked%20myself%20out.%20how%20do%20i%20get%20back%20in%3F\"><h3>I locked myself out. How do I get back in?<\/h3><\/dt>\n<dd><p>Wait for the lockout to end, or run <code>wp stoutwall unlock --all<\/code> with WP-CLI. To avoid it, add your office or VPN address under <strong>Settings \u2192 Stoutwall \u2192 Tools \u2192 Allowed IP addresses<\/strong>. Without WP-CLI, rename the <code>wp-content\/plugins\/stoutwall<\/code> folder, log in, rename it back and unlock the IP in <strong>Settings \u2192 Stoutwall \u2192 Tools<\/strong>.<\/p><\/dd>\n<dt id=\"everyone%20gets%20locked%20out%20at%20the%20same%20time\"><h3>Everyone gets locked out at the same time<\/h3><\/dt>\n<dd><p>Your site is behind a proxy or CDN, so every visitor reaches WordPress with the same IP address. In the <strong>Limit login attempts<\/strong> row, open <strong>Advanced<\/strong> and choose the header your proxy sets, for example Cloudflare.<\/p><\/dd>\n<dt id=\"will%20it%20break%20my%20site%3F\"><h3>Will it break my site?<\/h3><\/dt>\n<dd><p>Each measure can be turned off on its own. Things to know before enabling them:<\/p>\n\n<ul>\n<li>Disabling XML-RPC stops the Jetpack connection and the legacy WordPress mobile apps.<\/li>\n<li>Security headers stop other sites from showing yours inside a frame. Turn them off if you need that.<\/li>\n<li>HSTS makes browsers refuse plain HTTP for a year. Only turn it on when HTTPS works on every page.<\/li>\n<li>Requiring login for the REST API can break themes and plugins that load content through it. oEmbed, Contact Form 7, WooCommerce checkout and Jetpack stay public; use the <code>stoutwall_public_rest_namespaces<\/code> filter for others.<\/li>\n<li>Disabling registration overrides the \"Anyone can register\" setting.<\/li>\n<li>Blocking PHP in uploads affects any plugin that executes PHP files stored in the uploads folder, which is rare and usually a red flag.<\/li>\n<\/ul><\/dd>\n<dt id=\"what%20happens%20when%20i%20deactivate%20or%20delete%20the%20plugin%3F\"><h3>What happens when I deactivate or delete the plugin?<\/h3><\/dt>\n<dd><p>Deactivating removes the plugin's rules from the uploads <code>.htaccess<\/code> (other rules stay) and every other measure stops applying. Deleting the plugin also removes its settings.<\/p><\/dd>\n<dt id=\"i%20used%20protectmywordpress%20from%20github.%20how%20do%20i%20move%20to%20stoutwall%3F\"><h3>I used ProtectMyWordPress from GitHub. How do I move to Stoutwall?<\/h3><\/dt>\n<dd><p>Install and activate Stoutwall. It imports your ProtectMyWordPress settings and replaces its <code>.htaccess<\/code> rules. Then deactivate and delete ProtectMyWordPress.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>1.3.0<\/h4>\n\n<ul>\n<li>New: limit login attempts, with longer lockouts for repeat offenders, a list of locked-out IPs, allowed IPs and ranges, and support for Cloudflare and proxy headers.<\/li>\n<li>New: security headers (X-Content-Type-Options, X-Frame-Options, Referrer-Policy) and optional HSTS.<\/li>\n<li>New: switches to disable application passwords and to require login for the REST API (both off by default).<\/li>\n<li>New: server rules for Apache, LiteSpeed, OpenLiteSpeed and Nginx, generated from the protections you have on.<\/li>\n<li>New: read-only security audit with live tests for PHP in uploads and directory listing, also shown in Site Health.<\/li>\n<li>New: WP-CLI commands and settings export\/import.<\/li>\n<li>The X-Powered-By header is now also removed from the login page and the dashboard.<\/li>\n<li>Limit login attempts and security headers are turned on when you update. Turn them off in Settings \u2192 Stoutwall if you do not want them.<\/li>\n<\/ul>\n\n<h4>1.2.1<\/h4>\n\n<ul>\n<li>The plugin link now points to the WordPress.org directory page.<\/li>\n<\/ul>\n\n<h4>1.2.0<\/h4>\n\n<ul>\n<li>Renamed to Stoutwall for the WordPress.org Plugin Directory. Settings and <code>.htaccess<\/code> rules from ProtectMyWordPress are migrated on activation.<\/li>\n<li>Uploads: rules are written in their own block even when <code>uploads\/.htaccess<\/code> already exists. Before, an existing file meant no rules were added while the status still reported OK.<\/li>\n<li>Uploads: rules work on Apache 2.2 and 2.4 and also cover <code>.phtml<\/code>, <code>.php5<\/code>, <code>.phar<\/code> and double extensions. They are removed on deactivation.<\/li>\n<li>XML-RPC: pingbacks are now disabled too, and the <code>X-Pingback<\/code> header is removed.<\/li>\n<li>User enumeration: also covers the users sitemap, oEmbed, POST requests and lists such as <code>?author=1,2<\/code>. The posts list filter in the dashboard and the block editor author list work again.<\/li>\n<li>Generic login errors no longer hide registration errors.<\/li>\n<li>\"Hide username as display name\" now checks profile updates. Before, it read a field that the registration form does not have, so it never applied.<\/li>\n<li>The status check reports the state of each measure, translated, with clear labels.<\/li>\n<li>All strings are in English and ready for translation.<\/li>\n<\/ul>\n\n<h4>1.1<\/h4>\n\n<ul>\n<li>Fixed the settings form, the AJAX nonce and an XSS issue in notifications.<\/li>\n<li>Options are sanitized on save and assets load only on the settings page.<\/li>\n<\/ul>\n\n<h4>1.0<\/h4>\n\n<ul>\n<li>First release, as ProtectMyWordPress.<\/li>\n<\/ul>","raw_excerpt":"Lightweight WordPress hardening with simple switches: limit login attempts, security headers, block PHP in uploads, disable XML-RPC and more.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/396515","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=396515"}],"author":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/felipepitol"}],"wp:attachment":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=396515"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=396515"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=396515"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=396515"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=396515"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=396515"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}