Zamok – Security and Site Tools
Zamok – Security and Site Tools
Description
Zamok replaces a stack of single-purpose plugins — for admin enhancements, security hardening, SMTP email delivery, image optimization, database search-and-replace, database cleanup, and full-site backups — with one maintainable, modular package. Every feature is a toggle. Turn on what you need, leave the rest off.
About the name: Zamok (Замок) is Ukrainian for both castle and lock — strength and security in one word. The name is a small tribute to the people of Ukraine. 🇺🇦
Commitments
- 100% free and open source. GPL-2.0-or-later, forever. No “pro” version, no paid tier, no upsell, no ads.
- No tracking or telemetry. No usage statistics, no analytics, no phone-home, no self-updater. The only network connections it makes are ones you configure: your SMTP server and your off-site SFTP backup server.
- Lean by design. Modules load only when enabled; nothing runs that you haven’t turned on.
What it does
Zamok is fully modular. Every feature is a self-contained module you switch on or off from a single admin page, grouped into clear categories.
Core debloat
- Dashboard Widgets — removes all dashboard widgets and the welcome panel.
- Comments — completely disables the comment system; existing comments preserved.
- File & Site Editors — disables the Theme/Plugin File Editors and the Site Editor.
- Gravatars — disables Gravatar avatars to stop external requests to gravatar.com.
- Toolbar Cleanup — removes the WP logo menu, “+ New” menu, Help tab, and footer text.
- Disable REST API — blocks REST access for non-authenticated users.
- Disable Feeds — disables all RSS, Atom, and RDF feeds.
- Disable Embeds — disables oEmbed auto-discovery and the embed script.
- Disable Auto-Updates — turns off automatic core/plugin/theme updates.
- Disable Author Archives — returns 404 for author archives; prevents enumeration.
- Disable Archive Pages — returns 404 for category, tag, and date archives; filters them from the sitemap.
- Disable Smaller Components — removes version disclosure, legacy meta tags, emoji, frontend Dashicons, and jQuery Migrate.
- Disable XML-RPC — disables XML-RPC, removes the X-Pingback header, blocks pingbacks.
- Heartbeat Control — disables Heartbeat on the frontend and slows it in admin.
- Disable AI Features (WP 7.0+) — unhooks the AI Client, Abilities API, and Connectors.
- Disable Application Passwords — closes the Application Passwords auth surface.
- Limit Post Revisions — caps stored revisions per post (default: last 10).
- Strip Comment Author IP (GDPR) — stops WordPress storing commenter IPs.
Enhancements
- Email — SMTP delivery, a forced consistent From address, and a full email log with view/resend/auto-clean.
- Image Optimization — auto-resizes and converts new uploads to WebP using native WordPress image processing.
- Better Link Search — relevance ranking, clearer result labels, and a post-type filter in the link modal.
- Content Duplication — one-click duplicate for pages, posts, custom post types, and taxonomy terms. Copies all content, taxonomy assignments, custom fields, and term meta (including ACF fields).
- Media Replacement — replace a media file while keeping the same ID, date, and filename.
- SVG Upload — allows SVG uploads with automatic sanitization.
- Missed Schedule Fix — publishes scheduled posts that missed their time.
- Admin Notices Cleanup — hides plugin spam notices, keeps the important ones.
- Custom Login URL — changes the login URL from wp-login.php to a custom slug.
- Email-Only Login — restricts login to email addresses only.
- Site Identity on Login Page — replaces the WP logo/link with your site icon and URL.
- User Info Columns — adds Last Login and Registration Date to the Users list.
- Disable Gutenberg — restores the Classic Editor; removes block styles.
Security
- Two-Factor Authentication — TOTP authenticator app, emailed code, or single-use backup codes; enforced per role; fully self-hosted. Does not affect REST, XML-RPC, application passwords, WP-CLI, or cron.
- Brute Force Protection — locks out IPs after repeated failed logins, with escalating duration (1 hour, 6 hours, 24 hours, 1 week).
- IP Banning — blocks abusive IPs automatically (escalating, up to 7 days) plus manual bans, an allowlist, and a ban log. No permanent bans — entries expire and self-clean.
- System Hardening — server/filesystem hardening via .htaccess (protect system files, disable directory browsing, block PHP execution in writable dirs) and disables the dashboard file editor.
- Block User Enumeration — blocks ?author=N and gates the REST users endpoint.
- Admin Creation Alert — emails you the moment an administrator is created or a user is promoted to admin.
Tools
- Database Tools — operator-run utilities under Zamok Tools: a serialization-safe Search & Replace and a Database Cleanup for revisions, trash, spam, expired transients, and orphaned meta. Nothing runs on its own — every action is a manual click.
Backups
- Backups — full-site backup of files and database as a single encrypted package. Builds in resumable, timeout-safe steps so it works on shared hosting, with optional scheduling and off-site SFTP push. Archives are encrypted at rest with libsodium; both the browser download and the SFTP upload deliver a plain, restore-anywhere zip. Each package includes a standalone restore installer — just upload it, open in a browser, and follow the wizard.
Plugin-specific cleanup
- Clean Up Yoast SEO — removes promotional modals, upsell popups, menu bloat, the dashboard widget, admin bar menu, and premium upsell cards.
- Clean Up WooCommerce — removes marketplace suggestions, setup wizards, inbox notifications, payment install offers, and extension upsells.
Plugin-specific modules auto-disable when the target plugin is not active.
What it replaces
Zamok can replace the following plugins — gaining all their features while cutting admin page load times by ~20%, database queries by ~60%, and memory usage by ~25% (median results across 125 real before/after benchmarks; sites without WooCommerce typically see larger gains — around 21% faster loads and 62% fewer queries):
- WP Mail SMTP / Post SMTP Email module (SMTP, forced From, delivery log)
- Solid Security / Kadence Security / Wordfence Brute Force, IP Banning, Two-Factor, Login URL, System Hardening, User Enumeration
- Two Factor Authentication Two-Factor module (TOTP, email, backup codes)
- Smush / EWWW / ShortPixel Image Optimization module (WebP conversion)
- Safe SVG / SVG Support SVG Upload module (sanitized SVGs)
- Better Search Replace Database Tools (serialization-safe search & replace)
- WP-Optimize Database Tools (cleanup) + Heartbeat Control + Smaller Components
- Disable Comments Comments module
- Duplicate Post / Yoast Duplicate Post Content Duplication module
- Duplicate Taxonomy Terms (ACF) Content Duplication module (term duplication with full ACF field support)
- Duplicator / UpdraftPlus / All-in-One WP Migration Backups module (encrypted, scheduled, SFTP)
- WPS Hide Login Custom Login URL module
- Enable Media Replace Media Replacement module
For Developers: AI Image Marking
The AI Image Marking module records whether a media image is AI-generated using
the IPTC Digital Source Type vocabulary, preserves that marker through Zamok’s
WebP conversion, and exposes it to your theme. It renders NO front-end output by
design — the visible disclosure (your EU AI Act Article 50(4) duty as the site)
is the theme’s job. The module gives you the flag; you render the notice.
Stored values (attachment meta _zamok_ai_source):
- “ (empty) — not AI-generated
trainedAlgorithmicMedia— fully AI-generated from a prompt (no real photo input)compositeWithTrainedAlgorithmicMedia— AI applied to a real photo (real content was an input)
Read it (each is one cached read — pick whichever suits):
zamok_ai_source( int $attachment_id ): string — the value, or '' .
zamok_is_ai_generated( int $attachment_id ): bool — true when marked.
if ( function_exists( 'zamok_is_ai_generated' ) && zamok_is_ai_generated( $id ) ) {
// render your disclosure — figcaption, badge, data-attribute, whatever fits
}
Without a Zamok dependency, it also rides along on core metadata and REST:
$meta = wp_get_attachment_metadata( $id );
$ai = $meta['ai_source'] ?? ''; // key absent if Zamok is inactive
GET /wp-json/wp/v2/media/123 → { ..., "zamok_ai_source": "trainedAlgorithmicMedia" }
Note: the module preserves the plain IPTC/XMP marker through WebP conversion, but
it does not create signed C2PA Content Credentials (that requires a trust-listed
certificate, impossible from PHP), and installing it does not by itself make a
site compliant — the visible disclosure is yours to render.
Installation
- Upload the
zamokfolder to/wp-content/plugins/, or install the zip via Plugins Add New Upload Plugin. - Activate the plugin through the Plugins menu in WordPress.
- Open the new Zamok menu in the admin sidebar.
- Toggle on the modules you want.
Requires PHP 8.4 or higher and WordPress 7.0 or higher.
Screenshots

The Zamok modules page — toggle cards grouped by category.

The Email module: SMTP settings and the email log.

IP Banning: active bans and the ban log.

Two-Factor Authentication: per-role enforcement and the user setup wizard.

Database Tools: serialization-safe Search & Replace and Database Cleanup.

Backups: build a package, schedule, and push off-site over SFTP.
Faq
Yes. GPL-2.0-or-later, forever. There is no pro tier, no upsell, no feature locked behind a payment. We built this to replace plugins whose business model is upselling you — adding our own would defeat the point.
No. There is no usage tracking, analytics, telemetry, or licensing call-home. Everything runs on your own server. The only outbound connections are ones you configure and opt into: your SMTP server (Email module) and your SFTP server (Backups module). The backup worker makes a local loopback request to your site’s own admin-ajax.php to advance background jobs, and the standalone restore installer optionally fetches fresh salts from wordpress.org (with a local fallback).
Two-Factor is opt-in and defaults off. Backup codes are mandatory at setup, an administrator can reset any user’s 2FA from the user-edit screen, and the ZAMOK_2FA_DISABLE constant in wp-config.php is an emergency escape hatch.
Yes. SMTP, SFTP, and the backup encryption key can be pinned in wp-config.php via ZAMOK_SMTP_PASSWORD, ZAMOK_SFTP_PASSWORD / ZAMOK_SFTP_KEY, and ZAMOK_BACKUP_KEY. Secrets stored in the database are encrypted with libsodium.
Every module works on any server. The System Hardening module writes .htaccess rules, which apply on Apache/LiteSpeed; on Nginx those rules are inert and the documented Nginx snippets should be used instead.
Reviews
One of the best plugins so far
By f14design on August 4, 2026
Easy to use. And and fits in all first basics you need for you WordPress website! Awesome Job
Changelog
1.0.7
- Fix: on a shop where Zamok had been activated before WooCommerce, every product review, star rating and review form disappeared — whatever “Keep WooCommerce product reviews” was set to. Whether WooCommerce counted as present was decided while WordPress was still loading plugin files, so the answer came down to the order the two were activated in rather than to the shop actually running. That question is now asked when a page is built, by which point every plugin has loaded.
- Fix: saving the Zamok settings while WooCommerce was switched off silently turned “Keep WooCommerce product reviews” off with it, because the tickbox is only drawn while WooCommerce is active and an undrawn box reads as unticked. Switching WooCommerce back on then hid every review. The stored preference is now kept as it was.
- Fix: updating the plugin while a backup was running could finish that backup as an empty file and record it as complete. The update moves where a build keeps its working files, the running job then found none of its own, and nothing checked before writing the result. Worse, the empty backup counted as the newest good one, so housekeeping could delete a real older backup to make room for it. A build that cannot find its files now fails, and leaves every existing backup alone.
- Fix: if a prepared download went missing from the server — swept by a tidy-up job, a migration, a restore — the Backups screen showed “Preparing download…” indefinitely, with no link and no way to ask for it again, even though the backup itself was perfectly intact. It is now reported as failed, which offers the “Retry preparing download” action that already existed.
- Fix: saving the off-site SFTP form, or importing settings, silently broke SMTP. Both write the whole settings record back, which ran the stored SMTP password through the encrypting sanitizer a second time — so the password could no longer be decrypted and every email the site sent afterwards failed to authenticate, hours before anyone noticed. The sanitizer now recognises an already-encrypted value and leaves it alone.
- Security: the restore installer now asks for a restore key before it will do anything. Previously the only thing it asked for was the archive filename, which is always “archive.zip” and is printed inside the package, so anyone who reached installer.php during a restore could point the site at a database of their own. The key is shown on the Backups screen next to the download and is not stored anywhere in the package — write it down with your backup.
- Security: two-factor authentication can now also refuse XML-RPC logins from users who use it. xmlrpc.php signs in with a username and password alone and cannot ask for a second factor, so it was a way around 2FA entirely. New sites get this switched on. A site that already had two-factor switched on gets it off, and can turn it on itself: enabling it during an update would have stopped Jetpack and the WordPress mobile app signing in with a password on sites where nobody chose that. Application passwords are still accepted, and are now matched to the account they authenticated, so one application password can no longer wave through a different account’s login in the same XML-RPC request.
- Security: the plaintext database dump written during a restore is no longer called database.sql. On Nginx, where the installer’s protective rules do not apply, that fixed name meant the whole database could be downloaded by anyone during the import.
- Security: the trusted-proxy setting now accepts the proxy’s own address ranges, and honours forwarded client-IP headers only for requests that actually came from them. Set to any other truthy value it behaves as before and trusts whatever connects, which is spoofable whenever the origin server is reachable directly.
- Fix: the restore installer’s table-prefix field has been made read-only. Changing it produced a site whose configuration named a prefix that no table in the restored database used — an install screen or a database error after an apparently successful restore.
- Fix: mysqldump output is now checked before it is accepted as a backup. Only “the file is not empty” was verified, so a dump cut short by a memory limit, a full disk or a permissions error was stored as a complete backup. The exit status and the dump’s own completion marker are both required now, and anything else falls back to the built-in dumper with the reason recorded in the backup log. The database password is also no longer passed on the command line, where other users on a shared server could see it.
- Fix: the built-in database dumper now reads each table in primary-key order. Large tables are read across several passes, and without a fixed order the database gives no guarantee two passes agree — rows could be missed or written twice, and a duplicated row stops a restore.
- Fix: photos taken on a phone no longer come out sideways. WebP conversion ignored the rotation recorded by the camera, and because the converted file carries no such information the rotation was lost for good.
- Fix: replacing a large image with a smaller one could leave the attachment pointing at a file that had been deleted, breaking the image everywhere it was used.
- Fix: “Duplicate” is now offered on WooCommerce product categories and tags, and on taxonomies added by other plugins and themes. It previously appeared only on the ones built into WordPress.
- Fix: the Tables tool no longer offers to delete tables belonging to plugins it simply did not recognise. WPML, Rank Math, Wordfence, LiteSpeed, WP Rocket, Elementor, Smush and around forty others name their tables differently from their plugin folder and were all listed as unknown and deletable. Tables that still cannot be matched now require the table name to be typed out before they can be dropped, and on multisite the per-site tables of sub-sites are recognised properly.
- Fix: system hardening writes its rules, then asks the site for a page and takes the rules straight back out if the server answers with an error. On hosts that do not permit one of the directives, Apache returned a 500 for the whole site including the dashboard, with no way back for a non-technical admin.
- Fix: an email that failed to send is no longer announced on every screen in the dashboard. It now shows on Zamok’s own pages, with a count on the Email Log menu item.
- Fix: the SMTP conversation stored in the email log no longer contains the message itself. Every send stored the full body, and its attachments, regardless of the “store message body” setting.
- Fix: several hooks declared strict parameter types that another plugin could violate, which would stop the site with a fatal error rather than being ignored. This is the same fault as the login-URL crash fixed in 1.0.3, found in the sitemap, REST authentication, login and logout hooks.
- Fix: the restore wizard no longer fails with “Security check failed” partway through on connections whose address changes between requests, such as mobile networks and corporate VPNs.
- Fix: automatic bans on IPv6 now cover the /64 block the address sits in. A single IPv6 address is one of eighteen quintillion given to the same customer, so banning it stopped nobody.
- Fix: the addresses of the last ten administrator logins are protected from automatic bans, rather than only the most recent one. On a site with several administrators, a colleague became bannable again as soon as someone else signed in.
- Changed: new installs keep email log entries for 30 days instead of 90. Stored messages can contain password-reset links and customer details, and existing sites keep whatever they have set.
- Changed: new warnings on modules whose effects are invisible while you are logged in — Heartbeat Control (front-end page builders), Disable XML-RPC (Jetpack, the mobile app), Custom Login URL, Email-only login, Block User Enumeration, System Hardening, Image Optimization and Disable Application Passwords.
- Changed: the off-site SFTP settings now say plainly that the copy on the remote server is not encrypted.
- Improve: several small performance fixes — schema-version checks no longer cost an uncached database query on every page load, the hardening rules are no longer re-read from disk on every dashboard request, and the list of active bans loaded on every request is capped at a size that keeps it cheap.
- Security: plaintext files used while building backups and prepared downloads now prefer WordPress’s private temporary directory instead of uploads. Each job remembers its work directory across worker ticks and fails safely if that directory disappears; guarded uploads storage remains the fallback for hosts without a usable private temp directory.
- Changed: prepared downloads now expire after seven days to reclaim their disk space. Their size is shown on the Backups screen, where they can also be removed immediately without deleting the backup.
- Fix: downloading a large backup timed out. The restore package was assembled inside the download request itself — every encrypted part decrypted, re-merged and re-zipped before a single byte was sent. On a 342 MB backup that was 81 seconds of silence, which is past the default timeout of most web servers, so the browser was handed a gateway error while the server was still working. The package is now assembled in the background by the same resumable worker that makes the backup, and the download serves the finished file: the first byte now arrives in well under a second. The Download action shows “Prepare download” while a package is being built, with live progress, and becomes a normal download once it is ready.
- Fix: a package build that failed no longer marks the backup itself as failed, wipes its working files, or sends a scheduled-backup failure email. Only the package build is affected; the backup is untouched.
- Fix: preparing a package no longer blocks taking a new backup, and it now checks there is enough free disk space before it starts rather than failing partway through.
- Fix: leftover working files from a package build that was interrupted by the server (rather than by an error it could catch) are now cleaned up automatically on the next idle worker run, instead of staying on disk until the next build.
- Security: the prepared restore package is stored encrypted, like the backup it was made from. It is kept on the server until its backup is deleted, and the directory it sits in is protected by rules that Apache and IIS honour but Nginx ignores — so on an Nginx server an unencrypted copy would have been an unauthenticated download of every site file, the database and wp-config.php. It is decrypted while it downloads, so the download still starts immediately.
- Security: files written while a backup is being built were named from a predictable counter (build-1.sql), and the only thing protecting them was an Apache/IIS rule that Nginx does not read. During a build those files are not yet encrypted, so on an Nginx server an unauthenticated request could have retrieved the whole database and wp-config.php by guessing the name. Work files now carry an unguessable name derived from the site’s own secret keys.
- Changed: backups are encrypted in 8 MB frames instead of 25 MB, which lowers peak memory during backup and restore on smaller hosting plans. Existing backups are unaffected and still restore normally — nothing needs re-creating.
- Security: sending a backup to off-site storage no longer leaves a readable copy of it inside the uploads folder. The copy exists because the off-site file is deliberately stored unencrypted, so it can still be restored if the server is lost — but it was written under the uploads folder using the same name as the remote file, which is made from the site address and the date and so could be guessed on the first try. On a large backup that copy can sit there for hours while the transfer runs. It is now written to WordPress’s private temporary directory, which on most hosts is outside the website altogether, under a name derived from the site’s own secret keys, and it is deleted as soon as each part finishes or the transfer gives up. The same treatment has been given to the temporary folder used when downloading a backup that was split into several parts.
- Security: anything left behind by a backup that the server killed off mid-transfer is now cleaned up automatically on the next idle background run, including files left by earlier versions under the old predictable names. Previously an interrupted off-site transfer could leave a readable copy of the site on disk indefinitely.
- Fix: if the connection to the off-site server breaks during an upload, the connection is now thrown away and re-established rather than reused. A broken SSH connection can leave the two ends out of step, which made everything afterwards on that connection — including the check of how much had already been sent — unreliable.
- Fix: removed a web-server rule that could return a server error for the backups folder on older Apache installations, the same fault that broke the restore installer in 1.0.4.
- Security: the Tables tool no longer offers to delete tables that belong to a plugin, theme or library that is currently in use. It matched each table to a single plugin folder, and got it wrong whenever the folder name was not the obvious one — a plugin installed as both a free and a paid copy was judged by whichever copy it found first, so with the paid version running, every stored WPForms entry was listed as leftover data one click from deletion. A plugin whose tables are named differently from its folder, such as Connect for Mobilox, was not recognised at all; the WooCommerce job queue was attributed to a plugin that does not exist on its own; and no theme was ever considered. The tool now checks everything that is actually running before offering anything for deletion. On the test site this took the number of tables offered from 24 to 13, and all thirteen really are leftovers.
- Security: deleting a table that still contains rows now always requires typing its name. Only tables the tool could not identify at all asked for that, so a table it identified wrongly needed nothing more than clicking OK. Empty tables still use a normal confirmation.
- Fix: “Disable Feeds” now actually withholds the feed. It returned a 404 but still sent the complete feed — every post title, link and excerpt — so anything that does not check the status code, which is most scrapers and content-theft bots, received the lot. Addresses ending in /feed/ were blocked; the ?feed= form, which works on every WordPress site, was not.
- Fix: the tables list no longer labels WooCommerce’s own tables as WordPress core. The list of core tables was read from a value that plugins add themselves to, so the label depended on what had loaded and the same table could be labelled differently on different screens.
- New: a saved SMTP password can now be removed. Leaving the field blank keeps the existing password, which is right for a masked field but meant there was no way to say “no password at all” — an admin moving off SMTP or handing the site over could not clear it without editing the database. A “Remove the saved password” box appears whenever one is stored.
- Fix: the proxy warning now also appears on the Bans screen, which is where an address is actually typed and banned. Behind a proxy that has not been declared, every visitor shares one address, so a single ban there can lock out all of them. The warning was only on the module card.
- Fix: the export screen now says plainly that a settings file without credentials still contains the SMTP host, port and username. Only the password and the off-site connection are withheld.
- Fix: “Disable REST API” no longer stops a WooCommerce shop taking guest orders. The block cart and block checkout are built entirely on WooCommerce’s Store API, and blocking everything unauthenticated blocked that too — so a shop using blocks could only be checked out by someone already logged in, and nobody would notice while testing as an administrator. The Store API now stays open when WooCommerce is active; the administrative WooCommerce endpoints stay closed as before. Sites with other integrations can open further namespaces with the new zamok_rest_guest_namespaces filter.
- Fix: turning off comments no longer silently removes WooCommerce product reviews. Reviews are stored as comments, so switching comments off took every review, the review form and the star ratings with it, which is never what anyone meant by “disable comments”. A new “Keep WooCommerce product reviews” option, on by default, keeps reviews working on products while comments stay off everywhere else. Moderate reviews under Products, Reviews — the Comments screen is still hidden.
- Fix: status colours on Zamok’s screens now follow a dark dashboard. They were written directly onto each element, which overrides any stylesheet, so on a dark theme the green, red and amber stayed as they were and became hard to read. They are now ordinary styles a dark-mode plugin can adjust, and the Delete action keeps its warning colour either way.
1.0.6
- New: AI Image Marking module. Mark media-library images as AI-generated using the IPTC Digital Source Type vocabulary (fully generated vs. AI-applied-to-a-real-photo), for EU AI Act transparency and Google Merchant. Native Media list-view column and bulk actions, a per-image selector, and — critically — the marker is preserved through Zamok’s WebP conversion (GD otherwise strips all metadata). Provides a theme API (zamok_ai_source() / zamok_is_ai_generated(), a wp_get_attachment_metadata() [‘ai_source’] key, and a REST field); the visible on-page disclosure is left to the theme by design. See docs/ai-image-marking.md.
- Fix: backups corrupted every value containing a percent sign. The SQL dump escaped values with WordPress’s _real_escape(), which appends an internal placeholder token to protect literal % (it is meant to be used inside prepare(), where the token is stripped again). The dump wrote that token straight to the .sql file, so a % became a 66-character token — breaking values like “/%postname%/” and, critically, serialized data (a string’s stored length no longer matched its content), which made the whole backup fail to restore. The dump now strips the placeholder before writing. Backups taken before this fix are affected; restore them with the healed values or re-create the backup after updating.
- Fix: the portable installer download now works for large backups. A backup big enough to be split into multiple parts (over ~400 MB) previously refused to build its self-contained installer package and pointed to a download button that no longer exists, leaving large backups impossible to download from the admin. The installer package now merges all parts into one restore package.
- Fix: blank white admin page after a bulk action on WordPress 7.0. Its new admin View Transitions use attr()-based CSS that current Chrome skips on any admin redirect, leaving a blank frame; Zamok now dequeues that core stylesheet so admin pages load normally.
1.0.5
- New: WooCommerce module — an opt-in setting to disable the Analytics/Reports/Homescreen dashboard on stores that don’t use it, removing its background query and asset overhead from every WooCommerce admin page.
- Improve: WooCommerce module now throttles Action Scheduler’s background task-queue check (which otherwise runs on every single page load, admin or front-end) and skips an unnecessary database read for a payment-incentive banner that was already being hidden.
- Improve: Disable XML-RPC now blocks the xmlrpc.php endpoint outright — it returns 403 on every server, including Nginx, where the .htaccess-based rules don’t apply. Previously the module only stripped the pingback header and filtered methods, so xmlrpc.php stayed reachable and still answered the built-in system.* calls, which security scanners (and attackers) still saw as a live endpoint. It is now fully closed when the module is enabled.
- New: Database Tools Database Cleanup adds two WooCommerce-specific cleanup actions, shown only when WooCommerce is active: flipping onboarding-wizard and task-list options so they load only when needed instead of on every request, and removing already-actioned WooCommerce inbox notes (WooCommerce itself never deletes these once dismissed).
- New: clear warnings now appear on modules that can break things which aren’t obvious while you’re logged in — Disable REST API (payment webhooks, WooCommerce block checkout, contact forms), Comments (hides WooCommerce product reviews), and Disable Feeds (breaks RSS-based email campaigns and product feeds). The warning shows before you enable the module.
- Fix: Brute Force and IP Banning now detect when the site is behind a proxy or CDN (e.g. Cloudflare) and warn you to configure the trusted-proxy setting first — without it, every visitor is seen as the same IP, so one attacker could get that shared IP banned and lock out the whole site. A successful admin login can no longer store a proxy IP as “trusted”, which previously left banning ineffective.
- Fix: IP Banning no longer flags requests to /.well-known/ as suspicious — this is a standard, legitimate location (used by SSL certificate renewal and similar), and treating it as an attack could interfere with certificate issuance.
- Fix: System Hardening now also blocks direct access to license.txt (the WordPress licence file). It already protected readme.html, readme.txt and wp-config.php, but license.txt was left reachable — which security scanners flag as an information-disclosure item. The equivalent Nginx rule already covered it; the .htaccess rule now matches.
- Improve: System Hardening now also blocks other files that security scanners flag — wp-config.php backup copies (.bak/.save/.old/.orig/~), .env files, .DS_Store, debug.log and error_log, database dumps (.sql/.sql.gz) and stray editor/backup files. It deliberately does not block .zip/.tar archives, since many sites serve legitimate downloads. The Apache (.htaccess) and Nginx rule sets were also reconciled so both now protect exactly the same paths (a few rules previously differed between the two, and the PHP-execution block now also covers the php8/php9 and trailing-dot variants).
- Improve: Block User Enumeration is now self-contained — it also removes authors from the XML sitemap and strips author name/URL from oEmbed responses. Previously those two vectors were only closed if you also enabled the Disable Author Archives and Disable Embeds modules, so enabling this module alone left usernames harvestable.
- Fix: Disable Author Archives now stops ?author=1 style requests before WordPress can act on them. Previously a valid author id was redirected while an invalid one returned 404, so an attacker could still tell which account ids exist even though the author archive page itself was blocked.
- Fix: Disable Embeds now returns 404 for the per-post embed page (e.g. /your-post/embed/), and Comments now returns 404 for comment-feed URLs. Both modules previously only removed the discovery links from the page while the URLs themselves kept responding.
- Fix: the System Hardening .htaccess block is now always removed when the plugin is deleted, even when the “remove all data on uninstall” option is off — it is reversible server configuration, not data.
- Change: the “Disable Archives” module is now off by default; it returns 404s for category/tag/date archives, which is only desirable on sites that don’t use them.
- Change: System Hardening no longer blocks PHP execution inside the plugins and themes folders by default (this can break some plugins’ and payment gateways’ callback files); blocking PHP in the uploads folder stays on.
1.0.4
- Improve: the Insert/Edit Link search now finds category, tag and product-category archive pages — not just posts, pages and products. Each taxonomy gets its own filter pill so you can browse one at a time, and matching terms also appear inline as you search.
- Fix: choosing a link in the enhanced Insert/Edit Link modal now works inside Advanced Custom Fields (ACF) link fields — the selected link is saved to the field instead of being silently discarded.
- Fix: with the Custom Login URL module active, logging out no longer showed an extra “Do you really want to log out?” confirmation screen; logout now completes in a single click.
- Fix: the taxonomy filter control in the Insert/Edit Link modal is now readable when a dark-mode dashboard plugin is active.
- Fix: the portable restore installer failed on Apache servers that run PHP via FPM/CGI (for example Plesk and cPanel) — its protective
.htaccessused a directive only valid under mod_php and also blocked the installer’s own entry point, causing a 500 and then a 403 error. The installer now runs correctly on Apache as well as nginx. - Improve: redesigned the restore wizard with a cleaner, friendlier interface — a clear step tracker, guided wording, and a refined layout.
- Security: hardened the standalone restore installer — restore steps now run only in the correct, confirmed order, and the destination database password is encrypted while it is stored on disk during a restore.
- Security: the background backup worker can no longer be triggered by unauthenticated visitors; it now requires an internal one-time token. Scheduled backups are unaffected.
- Security: more reliable visitor-IP detection when the site sits behind a trusted reverse proxy, so spoofed forwarded headers cannot be used to evade IP bans.
1.0.3
- Fix: the Custom Login URL module could crash a site when another plugin generated an admin URL with a non-integer blog id — a strict type check on a WordPress
site_urlfilter argument. The filter now accepts the loose argument types WordPress passes.
1.0.2
- New: Backup retention is now tiered (GFS) — keep the N most recent plus daily / weekly / monthly / yearly backups, applied to both local and off-site copies. The newest backup is always kept.
- New: Backup storage modes — Local only, Mirror (local + off-site), or Off-site only (the local copy is removed after a verified upload, and downloads stream straight from your SFTP server). Off-site backups are stored as plain, directly-usable archives.
- Change: replaces the previous keep-last-N retention. Defaults retain at least as much as before, so existing sites are not pruned more aggressively on update.
1.0.1
- New: Database Tools Tables. Lists every database table with its size and the core feature or plugin it belongs to, and lets you delete leftover tables from inactive or removed plugins. Core and active-plugin tables are protected and cannot be deleted. Deletion is confirmation-gated and irreversible — back up first.
1.0.0
- Initial release — 41 toggleable modules across Core Debloat, Enhancements, Security, Tools, and Backups.
- GPL-2.0-or-later. No tracking, no telemetry, no paid tier.