Block WordPress REST API user enumeration without breaking the admin

Close-up of JavaScript code showing ajaxTransport, encodeURIComponent, and readyState functions — typical view of REST API client code (photo: Markus Spiske / Pexels)

By default every WordPress install since 4.7 leaks usernames over a public, unauthenticated REST endpoint. Anyone — no login, no auth header, just a browser — can hit https://yoursite.com/wp-json/wp/v2/users and get a JSON array of every user the site considers …

SSH key management for a multi-server fleet: ed25519, ssh-agent, ProxyJump, YubiKey FIDO2, and when agent forwarding is the wrong tool

You manage five servers. Each has a different SSH key in ~/.ssh/authorized_keys from the day you set it up — one’s an old id_rsa from 2018, two have your laptop’s current ed25519 key, one has an ancient ECDSA key from …

ssh-keyscan + known_hosts + StrictHostKeyChecking: doing it right in a personal-fleet shell script

Brass padlock and matching key on a granite surface — photo by theshantanukr on Pexels

You wrote a deploy script. It SSHes into 12 servers, runs an update, comes home. The first time you run it on a fresh laptop, every server prompts: The authenticity of host ‘203.0.113.x’ can’t be established. Continue connecting (yes/no/[fingerprint])?. …

Self-hosting Vaultwarden on a small VPS: docker-compose, Caddy reverse proxy, and migrating from a paid Bitwarden plan

You’ve been paying $40/year for a Bitwarden family plan, or $36/year for an individual one. The product is excellent — there’s nothing wrong with what they’ve built. But you also have a $5/month VPS that’s already running a couple of …

The wp_options.siteurl hijack: how a one-row UPDATE redirects every visitor and how to spot it before Google does

Metal directional arrow plate on a wooden floor — photo by Max Laurell on Pexels

One of the simplest, oldest, and still most effective WordPress compromises is a single SQL update. The attacker gets one query into your database — through any RCE, SQLi, or stolen-credential path — and runs:

UPDATE wp_options
   SET option_value = 

fail2ban vs CrowdSec on a small VPS: where the rule sets overlap, where they fight each other, and how to pick one without re-banning everything

Terminal screen showing system logs and security monitoring output — photo by Tima Miroshnichenko on Pexels

I’ve been running fail2ban on this Oracle box since the day I provisioned it. Six months ago I added CrowdSec because I wanted the community blocklist for SSH brute-force IPs. For three months they coexisted and I assumed it was …

Block PHP execution in wp-content/uploads on OpenLiteSpeed: the right .htaccess snippet

Computer monitor displaying terminal output: system metrics, file listings, and kernel error messages — typical sysadmin view (photo: Tima Miroshnichenko)

wp-content/uploads/ is the most predictable target on a WordPress install. It’s writable by the web server (so any compromise that gets a file uploaded lands here), it’s almost never inspected by malware scanners with the same vigilance as wp-includes/, …

The .hph extension trick: how WordPress malware survives cleanups by shadowing .php files

Four nearly identical white binders standing in a wooden box, suggesting how easy it is to overlook a slightly differently-named file in a directory listing (photo: Mateusz Dach / Pexels)

You clean a WordPress malware infection. You find every .php file with the suspicious signature, quarantine it, restore from backup, harden the site. Three weeks later the same backdoor is back. Same filename, same content, same behavior. You’re sure you …