Michael Dyrynda
Home Blog Podcasts
 
Protecting Laravel Cloud endpoints June 24th, 2025

It's something that likely affects everybody's application on the internet - bots scanning sites for vulnerabilities, looking for exposed credentials, out-of-date WordPress installs, and who knows what else.

For the most part, those requests can be safely ignored; after all, if you don't have WordPress endpoints in your app, they can't be exploited.

Given the way that Laravel Nightwatch ingests data, where everything - from jobs, to exceptions, to requests - is treated as an event for the purposes of both observability and billing, if you get a lot of these scanning requests (or even a modest amount across a lot of of endpoints), you might find your quota consumed quite quickly.

Sudden event consumption

If you're hosting your application on Laravel Cloud, you might have noticed that they even have some rules in place to protect your application from these kinds of requests.

Laravel Cloud blocking requests

When triggered, the client will receive a standard nginx 403 page.

Whilst this protection is good, it's falling short in a couple of areas for me:

  1. It's only a drop in the pond in terms of "all" the bad requests I want to stop
  2. I believe because it's still being served by Cloud, it's considered a request for billing purposes there

In addition, if it's not one of the rules covered by Cloud, those requests still wind up as unmatched routes in Nightwatch and consuming requests.

This is where Cloudflare's WAF custom rules come into play.

Two important things to know:

  1. (Perhaps obviously) you must be using Cloudflare for DNS
  2. You must be using a custom domain with a CNAME to Laravel Cloud for both the apex domain and any subdomains you want to protect. A-records seem to be ok if you're hosting elsewhere, like Laravel Forge.

Note that while I'm specifically using Laravel Cloud, this will apply to any host behind Cloudflare.

These rules apply per domain, so once you're into the dashboard for the domain you want to manage, click on Security > WAF > Custom rules.

Click Create rule, name the rule - mine is creatively named "Bot scanning", then build out your rules either with the expresison builder, or manually.

I've entered my rules manually, as I get a bit more control over what I want to block, and order of operations. Below is my ruleset at the time of this post.

(
    (
        http.request.uri.path contains "/." and not
        http.request.uri.path contains "/.well-known/"
    ) or (
        http.request.uri.path contains "/_" or
        http.request.uri.path contains "/administration/" or
        http.request.uri.path contains "/administrator/" or
        http.request.uri.path contains "/adm" or
        http.request.uri.path contains "/admins/" or
        http.request.uri.path contains "/apache" or
        http.request.uri.path contains "/api/" or
        http.request.uri.path contains "/app/" or
        http.request.uri.path contains "/wp-login.php" or
        http.request.uri.path contains "/wp-admin" or
        http.request.uri.path contains "/wp-content" or
        http.request.uri.path contains "/wp-includes" or
        http.request.uri.path contains "/wp-json" or
        http.request.uri.path contains "/wp-config.php" or
        http.request.uri.path contains "xmlrpc.php" or
        http.request.uri.path contains "composer.json" or
        http.request.uri.path contains "composer.lock" or
        http.request.uri.path contains "web.config" or
        http.request.uri.path contains "phpinfo" or
        http.request.uri.path contains "PHPMailer" or
        http.request.uri.path contains "YiiMailer" or
        http.request.uri.path contains ".." or
        http.request.uri.path contains "//"
    ) or (
        http.request.uri.path wildcard "*.ashx" or
        http.request.uri.path wildcard "*.aspx" or
        http.request.uri.path wildcard "*.ini" or
        http.request.uri.path wildcard "*.log" or
        http.request.uri.path wildcard "*.sh" or
        http.request.uri.path wildcard "*.txt"
    )
) and (
    http.host eq "example.com" or
    http.host wildcard "*.example.com"
)

I've separated this into three blocks

  • Dotfiles (blocks .env, .mailtrap, etc.), but explicitly allows .well-known, which is used for legitimate purposes such as TLS certificate verification.
  • http.request.uri.path contains - partial matches, good for entire route namespaces like /api or /admin
  • http.request.uri.path wildcard - good for blocking certain file extensions, irrespective of what precedes them

Lastly, there is a block that checks the http.host both at the apex (example.com) and any wildcard subdomains (*.example.com).

Next, set the action to Block and set place at as First, then save the rule and you should be good to go.

If you were to hit one of these myriad endpoints, you'll now get a Cloudflare 403 page.

A prettier Cloudflare 403

Leveraging WAF custom rules means that you can block traffic to your site for endpoints that don't exist.

You're not going to spend compute on your origin server spinning up the framework to return a 404 or burn through event quota that you'd rather be saving for tracking real issues in your application.

I'm a real developer ™
Michael Dyrynda

@michaeldyrynda

I am a software developer specialising in PHP and the Laravel Framework, and a freelancer, blogger, and podcaster by night.

Proudly hosted with Vultr

Syntax highlighting by Torchlight