Why WordPress bots keep knocking on your Java server

Last Refreshed // 2026-01-21

Running a server online comes with a lot of risks. Without proper security, unauthorized users can access sensitive endpoints or disrupt services.
In this post, we’ll walk through a simple practical example of basic protection of a Java server (like this blog) and show how to implement a basic anti-bot protection strategy.

Java defense

How to block bots and scanners that are looking for WordPress on your Java servers

If your Spring Boot application has never run PHP, never installed WordPress, and never will — but your access logs are still full of requests for wp-login.php — congratulations.

You’re not under attack.
You’re just on the internet.

Let’s talk about why this happens, when it matters, and how to deal with it the right way — without wasting JVM threads, flooding your logs, or turning your app into a bot-trolling science project.

Why bots look for WordPress on Java servers

Most WordPress scanners are aggressively dumb by design.

They don’t:

  • Detect your tech stack
  • Read your HTML
  • Learn from failures

They do:

  • Sweep IP ranges
  • Try the same list of paths everywhere
  • Move on immediately if nothing responds

To them, your Java server is just another door handle to jiggle.

The first rule: don’t fight bots in your controllers

If a bot request reaches your Spring controller:

  • A JVM thread woke up
  • Your app did work
  • Your logs got noisier

That’s already more attention than the request deserved.

Yes, you can handle scanners in Spring Boot — with virtual threads, async handlers, or clever routing — but in production, that should be your last line of defense, not the first.

The right place to solve this: Nginx (if you don't have a WAF)

If your app doesn’t serve PHP, then any request ending in .php is invalid.

You don’t need a list of WordPress paths.
You don’t need bot detection.
You just need one rule.

Block All .php Requests with 410 Gone

# Java app, no PHP here 
location ~* \.php$ { 
    return 410; 
} 

That’s it.

  • No JVM involvement
  • No application logs
  • No bandwidth wasted past the proxy

Why 410 instead of 404?

  • It’s explicit
  • It’s cacheable
  • Scanners lose interest faster

Also block common “secret” files

Bots love guessing config files too.

location ~* ^/(\.env|\.git|\.svn|phpinfo\.php)$ { 
    return 410; 
} 

Again: fast, boring, effective.

Keep this noise out of your access Logs

There’s no reason to log requests you’ve already decided to ignore.

map $request_uri $loggable { 
    default 1; 
    ~*\.php$ 0; 
    ~*^/(\.env|\.git|\.svn|phpinfo\.php)$ 0; 
} 
 
access_log /var/log/nginx/access.log combined if=$loggable; 

Now your logs contain things worth reading.

Optional: slow down everything else

If a scanner starts guessing paths instead of filenames, rate limiting makes it pointless.

limit_req_zone $binary_remote_addr zone=botlimit:10m rate=5r/s; 
 
server { 
    location / { 
        limit_req zone=botlimit burst=10 nodelay; 
        proxy_pass http://spring_boot_upstream; 
    } 
} 

This protects your app without affecting normal users.

What About Handling This in Java?

Yes, you can, this could be required if you don't have a proxy or you don't use a solution like Cloudflare.

  • Java 21 virtual threads make tarpitting safer
  • Async handlers prevent thread exhaustion
  • Spring Security can short-circuit fake paths

These are useful tools — but they’re best reserved for defense in depth, not primary protection.

If Nginx can answer the request, your JVM shouldn’t even know it happened.

Spring Security Fallback: Defense in Depth

If you’re not using Nginx (or want an extra layer of protection), you can block .php requests directly in Spring Security. This is useful for:

Apps running behind proxies you don’t control
Cloud environments where edge rules are limited
Extra peace of mind.

import org.springframework.context.annotation.Bean; 
import org.springframework.context.annotation.Configuration; 
import org.springframework.security.web.SecurityFilterChain; 
import org.springframework.security.web.util.matcher.AntPathRequestMatcher; 
import org.springframework.security.web.util.matcher.OrRequestMatcher; 
import org.springframework.security.web.util.matcher.RequestMatcher; 
import org.springframework.http.HttpStatus; 
 
@Configuration 
public class BotBlockerConfig { 
 
    @Bean 
    public SecurityFilterChain botBlockerFilterChain(HttpSecurity http) throws Exception { 
        // Match all .php requests and common scanner paths 
        RequestMatcher phpMatcher = new AntPathRequestMatcher("/**/*.php"); 
        RequestMatcher envMatcher = new AntPathRequestMatcher("/.env"); 
        RequestMatcher gitMatcher = new AntPathRequestMatcher("/.git/**"); 
        RequestMatcher svnMatcher = new AntPathRequestMatcher("/.svn/**"); 
        RequestMatcher phpInfoMatcher = new AntPathRequestMatcher("/phpinfo.php"); 
 
        RequestMatcher blockedRequests = new OrRequestMatcher( 
            phpMatcher, 
            envMatcher, 
            gitMatcher, 
            svnMatcher, 
            phpInfoMatcher 
        ); 
 
        http 
            .securityMatcher(blockedRequests) 
            .authorizeHttpRequests(auth -> auth 
                .requestMatchers(blockedRequests).denyAll() 
            ) 
            // Return 410 Gone for blocked requests 
            .exceptionHandling(handling -> handling 
                .accessDeniedHandler((request, response, accessDeniedException) -> { 
                    response.setStatus(HttpStatus.GONE.value()); 
                }) 
            ); 
 
        return http.build(); 
    } 
} 

Why this works?

No JVM thread wasted: The request is rejected before reaching your controllers.
Consistent with Nginx: Returns 410 Gone for uniformity.
Extensible: Add more RequestMatcher patterns as needed.

When to use this?

As a secondary layer if you already have Nginx rules.
If you’re running in a serverless or managed cloud environment.
For legacy apps where you can’t modify the proxy config.

The “Do Nothing” option

Sometimes the most professional response is:

  • 410 Gone
  • No warning logs
  • No alerts
  • No emotional investment

A well-configured server doesn’t panic when an annoying script knocks on the door.

The PRO solution

If you are building an enterprise solution that brings the salary to you and your colleagues, you shold not play with all the bots on the web.

Using a WAF (Web Application Firewall) or CloudFlare are the recommended solutions.

The FUN solution

It's probably not effective and not worth the effort, but it's funny. You could redirect all the 'bad' requests to a video on YouTube.

Terminal to youtube

TL;DR

  • WordPress bots scanning Java servers is normal
  • Don’t handle garbage traffic in your controllers
  • Return 410 for all .php requests at the proxy
  • Use a WAF or CloudFlare if this is not a pet project
  • Keep logs clean
  • Let the JVM do real work

The modern web is noisy. Most of that noise isn’t aimed at your server — it’s just passing through trying to annoying everybody.

Handle it early and quietly.