What's a "normal" amount of failed login attempts? Trying to calibrate before I over-engineer

Fairly new to running my own WordPress site and I got a fright the first time I opened the login logs, dozens of failed attempts a day from IPs all over the place. From what I've read since, this is mostly background noise: automated bots spraying credentials at the default login on basically every install, and unless the numbers are huge or paired with other symptoms it isn't a targeted attack. I tightened the obvious things, a strong unique password, a second factor on the admin account, and capping how many tries an IP gets before it's locked out, and the volume dropped noticeably.

Two things I'd love a reality check on from people who've run sites longer: at what point should a spike in failed logins actually worry me versus just being noise, and is limiting attempts + 2FA genuinely enough on its own, or is there one more thing you'd treat as non-negotiable? Don't want to bolt on five plugins for a problem that's already handled.

reddit.com
u/Capital_Attention702 — 16 hours ago

Do you actually hide your wp-login URL, or just rate-limit it?

Been going back and forth on this one. My logs fill up with failed logins on the default /wp-login.php every day, as far as I can tell it's just bots hitting the same path they hit on every WordPress site, nothing targeted at me. Limiting attempts and adding a challenge to the form quieted most of it, but I keep wondering whether moving the login off the default path is genuinely worth it or just security-through-obscurity that breaks more than it fixes.

For those of you who've hidden the login URL: did it actually cut the noise in your logs, or did it mostly create headaches with things that expect wp-login.php (mobile app, integrations, the odd plugin)? And if you deliberately don't hide it, what's your reasoning, rate-limiting at the edge and calling it done? Trying to figure out where the real payoff is before I fiddle with it further.

reddit.com
u/Capital_Attention702 — 16 hours ago

Everyone's arguing auto-updates on vs off. The thing that actually got people this month was the update itself

https://preview.redd.it/krk2vsiajzih1.png?width=1762&format=png&auto=webp&s=379a2494f2b3d48cad954ad9e1b770de023c1f6e

The 7.0.x releases have restarted the oldest argument in this sub. Auto-updates on, because a patched hole beats a broken layout. Auto-updates off, because you don't push untested code to production at 4am. Both camps have been making the same case for a decade, and both are working from an assumption I think is the actual problem.

Both assume the update itself is safe.

The case for patching fast is strong and I'm not arguing against it. Patchstack's State of WordPress Security in 2026 puts the weighted median time from disclosure to first exploitation at five hours, across 11,334 vulnerabilities disclosed in 2025, 91% of them in plugins. If you're patching on a weekly review cycle you are losing that race, every time, and the 4am-broken-layout crowd is trading a rare outage for a common breach.

Here's the number from that same report that nobody quotes though. 46% of those vulnerabilities had no patch available at the moment they were disclosed. Patchstack's own conclusion is that this "shows why website owners can't rely on plugin updates as a security measure." Their words, not mine.

But look at what the update actually is. Your site fetches a package from a vendor's server, unpacks it, and executes it. That's a write path the site trusts by design. There's no authentication event anywhere in that chain to harden and no second opinion on what arrived. 2FA, lockouts, a firewall sitting in front of the login page, none of them are in that request path at all.

That's not hypothetical this month. An agency posted a writeup here on the 3rd describing their plugin vendor's update server being compromised and a backdoor landing on client sites through a routine update. Someone in another thread described a window in early August where pressing update in wp-admin could hand you a tampered package. Both are those posters' accounts and neither is independently verified, so treat the specifics as claims. The shape is what I'm interested in.

So my position is that both sides of the argument are defending the wrong door. The useful question isn't auto or manual. It's how you'd know.

There is a partial answer built into WP-CLI that hardly anyone runs. wp plugin verify-checksums compares the plugin files on your site against the checksums wordpress org publishes for that version. If a file changed after install, it says so.

Then you hit the part that matters here. It only works for plugins from the repo. Point it at a premium plugin and you get "could not retrieve the checksums, skipping," because there is no published source of truth to compare against. The vendor ships the package themselves.

So the one integrity check built into the tooling covers everything except the category where vendor-delivered tampering actually happens. In the ShapedPlugin case reported in June, the backdoor went into the Pro builds on 21 May and the first customer reports landed on 10 June. Twenty days, three paid plugins, delivered through the vendor's own update system. That timeline is from Wordfence's data as reported at the time, not something I measured.

Which brings me to the part I don't have a good answer for, and the reason I'm posting.

If a plugin update you installed last month had been tampered with in transit, how would you find out? Not in theory. What in your current setup would actually tell you?

reddit.com
u/Capital_Attention702 — 8 days ago

Why your security plugin shows "blocked attacks" for plugins you never installed

This comes up every few weeks, and the answer never seems to be wherever people go looking for it. So, here.

You open your firewall summary and find something like:

Blocked for [Plugin Name] <= 2.1.4 - Unauthenticated Sensitive Information
Exposure via REST API in query string: rest_route = /[plugin-slug]/v1/tests/mock-data

You have never installed that plugin. It isn't in your plugins folder, it isn't sitting there deactivated, it was never there at all. Two things are getting confused here, and separating them makes the whole thing boring, which is the correct outcome.

The request is generic. WordPress serves REST routes at /wp-json/..., and it also accepts ?rest_route=... as a query-string fallback so the API still works when pretty permalinks are off. That fallback resolves on every WordPress install. So a bot needs to know nothing about your site to try it. It takes a list of recently disclosed plugin vulnerabilities, builds the request for each one, and fires the whole list at every WordPress site it can find. The sites running that plugin answer with something useful. The rest return nothing, because the route was never registered.

The block is a pattern match, not a detection. Your firewall recognized the shape of the request and stopped it before WordPress got a chance to shrug at it. That's why the log names a plugin and a version range: it's describing the exploit the request was written for, not something it found on your site. The phrasing makes it read like you were targeted and narrowly got away with it. You weren't, and there was nothing to get away from.

So: nothing is installed that shouldn't be, there's nothing to clean up, and it isn't related to some other plugin of yours with a similar name.

What the alert does tell you is that your site is on somebody's list. About 91% of last year's disclosed WordPress vulnerabilities were in plugins rather than core, per Patchstack's 2026 report, so those lists are long and they get worked constantly. Being on one only means your site answered a WordPress fingerprint check at some point.

If you'd rather be on fewer of them, look at what an anonymous request can learn about your install. Version strings hanging off your CSS and JS URLs. Readme files sitting under plugin directories. Directory listings nobody turned off. None of that is secret and none of it is why anyone gets hacked. It's just what makes a site cheap to sort into "worth coming back to" rather than "no idea what this is."

reddit.com
u/Capital_Attention702 — 8 days ago

Your magic-link login "randomly" fails for some users. It's their employer's mail scanner burning the token.

This one comes up in support and almost never gets diagnosed correctly, because the person reporting it and the person debugging it are never on the same network.

The symptom: you ship magic-link login. Most users are fine. A subset report that the link "doesn't work" or says it expired. You click the same link from your own inbox and it works every time. You can't reproduce it, so it quietly gets filed as user error.

What's actually happening: the user's employer runs inbound mail security. Those products fetch every URL in a message before delivery, to see where it lands. Your token endpoint has no way to tell that request apart from a human clicking. So if you implemented single-use tokens properly, the scanner redeems the token, the token dies, and the human then clicks a link that is now genuinely expired. The more correct your implementation, the more reliably it breaks.

The tell is that it clusters by email domain. Consumer mailboxes are fine, corporate ones are not, and it will look random until you group the failures by domain.

There is no clean fix, only tradeoffs:

  • Require a POST or a click-through interstitial before redemption. Scanners generally don't submit forms. Costs your user a click.
  • Bind the token to the session that requested it. Breaks the request-on-desktop, open-on-phone flow, which is half of why people want magic links.
  • Allow a couple of redemptions inside a very short window. Weakens single-use, which was the point of single-use.
  • Short lifetime plus an obvious "send me a new link" path. Doesn't fix it, makes it survivable.

While you're in there, two settings that ship as defaults nobody revisits. Token lifetime should be minutes rather than hours, because these sit in mailboxes and forwarded threads for as long as you let them. And the request-a-link endpoint needs a rate limit, because you just added an unauthenticated endpoint that will happily work as a mail bomber and a username-enumeration oracle at the same time.

The larger thing worth saying to whoever signed off on this. Magic links do delete the password-reset queue, and that's a real win. What they do in exchange is move the account's security into a mailbox you don't control, with no second factor behind it. A compromised inbox is a compromised account, silently, and the user gets no signal it happened.

That's a legitimate trade. It should just be a decision someone made on purpose, rather than a side effect of picking whichever plugin had the nicest onboarding.

u/Capital_Attention702 — 8 days ago

Where you enforce AI crawler rules decides whether they do anything for your server

Most AI crawler threads here argue about whether to refuse the bots. Fewer talk about where the rule runs, and on WordPress that second question is the one that decides whether your CPU graph changes.

Three places a rule can live, in order of how early the request dies:

Server or edge, at the rewrite layer. The request is refused before PHP is invoked. Nothing in WordPress runs. This is the only version that actually gives you the resource back.

A plugin. PHP has already booted, WordPress has loaded, plugins have initialised, and then something returns a 403. The bot goes away and your server did the work anyway. Fine for policy, close to useless for load.

robots.txt. Nothing is enforced at all. It is a declaration that compliant crawlers choose to honour, which means it governs exactly the bots that were already going to behave.

That ordering matters more on WordPress than on a static site, because an uncached request here tends to boot the whole stack before anything is served. A crawler that ignores your robots file and gets a plugin-level 403 costs you nearly the same as one you let through.

The other half is the decision itself. Each major AI vendor runs several crawlers with different jobs: one that collects content for training, one that fetches your page so an assistant can cite it with a link, and one that runs when a person pastes your URL into a chat. They are separate names, and a blanket rule catches all three. Refusing the first costs nothing. Refusing the second removes you from answers people are now searching in.

How do you handle it, robots.txt only or actual server rules?

reddit.com
u/Capital_Attention702 — 16 days ago
▲ 2 r/u_Capital_Attention702+1 crossposts

Relocating wp-config.php outside the web root is good advice, but not for the reason most people give

Every WordPress hardening thread eventually says move wp-config.php above the web root. It's good advice. I just think most people repeat it for the wrong reason, and that leads to a false sense of security.

Why it helps: a direct request to /wp-config.php has nothing to serve if the file isn't under the web root. A misconfig or a stray backup that would otherwise hand it over as plain text now finds nothing. Real win.

Why it helps less than people think: any code running as your web user can still read that file wherever you put it. WordPress reads it every request. So does a vulnerable plugin with a file-read or LFI flaw. Relocating the file does nothing against that route, and that route is the common one. Most disclosed WordPress vulnerabilities live in plugins, not core. Patchstack put it at 91% for 2025.

So the honest version of the advice is: relocate it, set owner-only permissions, keep your plugin list short and patched, and rotate your salts if you ever suspect exposure. Relocation is one lock on a door that has several.

The step I see skipped most is rotating the AUTH keys and salts after a scare. New database creds in the file mean nothing if the old session tokens still validate.

Curious how others handle this. Do you bother moving wp-config, or do you lean entirely on file permissions and a tight plugin set?

reddit.com
u/Capital_Attention702 — 23 days ago

Why your security plugin shows "blocked attacks" for plugins you never installed

This comes up every few weeks, and the answer never seems to be wherever people go looking for it. So, here.

You open your firewall summary and find something like:

Blocked for [Plugin Name] <= 2.1.4 - Unauthenticated Sensitive Information
Exposure via REST API in query string: rest_route = /[plugin-slug]/v1/tests/mock-data

You have never installed that plugin. It isn't in your plugins folder, it isn't sitting there deactivated, it was never there at all. Two things are getting confused here, and separating them makes the whole thing boring, which is the correct outcome.

The request is generic. WordPress serves REST routes at /wp-json/..., and it also accepts ?rest_route=... as a query-string fallback so the API still works when pretty permalinks are off. That fallback resolves on every WordPress install. So a bot needs to know nothing about your site to try it. It takes a list of recently disclosed plugin vulnerabilities, builds the request for each one, and fires the whole list at every WordPress site it can find. The sites running that plugin answer with something useful. The rest return nothing, because the route was never registered.

The block is a pattern match, not a detection. Your firewall recognized the shape of the request and stopped it before WordPress got a chance to shrug at it. That's why the log names a plugin and a version range: it's describing the exploit the request was written for, not something it found on your site. The phrasing makes it read like you were targeted and narrowly got away with it. You weren't, and there was nothing to get away from.

So: nothing is installed that shouldn't be, there's nothing to clean up, and it isn't related to some other plugin of yours with a similar name.

What the alert does tell you is that your site is on somebody's list. About 91% of last year's disclosed WordPress vulnerabilities were in plugins rather than core, per Patchstack's 2026 report, so those lists are long and they get worked constantly. Being on one only means your site answered a WordPress fingerprint check at some point.

If you'd rather be on fewer of them, look at what an anonymous request can learn about your install. Version strings hanging off your CSS and JS URLs. Readme files sitting under plugin directories. Directory listings nobody turned off. None of that is secret and none of it is why anyone gets hacked. It's just what makes a site cheap to sort into "worth coming back to" rather than "no idea what this is."

reddit.com
u/Capital_Attention702 — 1 month ago

PSA: if you renamed your wp-login URL and you're still getting login attempts, it's almost never a breach

This comes up constantly, so I'll write it once.

You rename your login slug, and minutes later you're still seeing failed login attempts. The instinct is "the secret leaked, something's compromised." Almost always, it hasn't.

Two things are usually going on.

  1. WordPress has more than one login. /xmlrpc.php exposes a method, wp.getUsersBlogs, that takes a username and password and validates them with no login form involved. system.multicall even lets a bot batch hundreds of guesses into a single request. Renaming /wp-login.php does nothing to that endpoint, so the attempts keep coming and it looks like your new URL got discovered. It didn't. The bot was never using it.
  2. If the attempts come from constantly changing IPs, rate limiting and per-IP lockout can't do much. They count failures per address, and a rotating-IP botnet sends each try from a fresh IP, so the counter never trips. You can drop the threshold to 2 and nothing changes.

What actually quiets it down:

  • Change the login path at the rewrite layer (nginx/apache config), not just with a slug plugin, so a request to /wp-login.php returns 404 before PHP loads. No process starts, so the rotating IPs stop mattering.
  • Disable XML-RPC if nothing you run needs it (Jetpack and some old mobile apps are the exceptions), or at least block the auth methods.

None of this replaces a security plugin for 2FA and monitoring, those still earn their place. It just moves the brute-force defense to a layer the bots can't walk around by swapping an IP or an endpoint.

For those of you who renamed the login and still saw attempts: did you ever check your access log for /xmlrpc.php hits? Curious how often that's the real source versus something else.

reddit.com
u/Capital_Attention702 — 2 months ago