Stop Wasting Monitoring Quota: Block Malicious 404s with NGINX

3 min read

The Problem

When I first integrated Laravel Nightwatch into my application, the actionable insights were great — until my free monitoring quota started getting wiped out. Why? Malicious bots probing for vulnerable files and endpoints, triggering countless 404s in Laravel:

/wp-login.php  
/.env  
/server-status  
/aws/credentials  

My app returned 404s, but Nightwatch still counted them—and so did my logs.

Not Just Me: Inspiration from Michael Dyrynda

I discovered Michael Dyrynda’s blog post, where he tackled the same issue on Laravel Cloud. He used Cloudflare’s WAF for Bot Management to protect critical endpoints with a custom rule by blocking or challenging malicious requests before they ever reached his infrastructure.

What stood out:

  • Cloudflare WAF’s Bot Management can block based on bot score or known patterns
  • You can also set custom path rules (e.g., blocking /actuator or XML-RPC endpoints)
  • This approach works regardless of hosting environment — you just need Cloudflare proxy enabled

When Cloudflare WAF is a Great Option

If you’re using Cloudflare, even on a non-Laravel Cloud setup, their WAF and Bot Management offer powerful, centralized controls:

  • Custom firewall rules or managed rulesets to block or challenge bots
  • Bot scoring to throttle or drop highly automated traffic
  • No changes needed to your origin server — rules are enforced at Cloudflare’s edge
  • Even Free/WAF plans include powerful defenses like OWASP rules, Bot Fight Mode, and AI-bot blocking

So yes — Michael’s Cloudflare-based solution is a completely viable and elegant option for many setups. I would encourage this as a way to centralise your bot control.

Why I Chose NGINX Instead

That said, my environment is a self-managed VPS with NGINX, and:

  • I didn’t want to rely on Cloudflare — especially for teams not already using it
  • I needed something lightweight, portable, and hostname-agnostic
  • I wanted to deploy across any server running NGINX without extra services

The result? A simple blocked-routes.conf snippet that stops malicious paths before they reach Laravel or Nightwatch.

🛠 NGINX Blocking Snippet

Grab the full blocklist from my public Gist: View the full config on GitHub Gist

Here’s a preview:

# =====================================================
# NGINX Bot & Exploit Path Blocklist
# Prevents malicious scanners from hitting Laravel
# Returns 444 (no response) to save server + Nightwatch load
# Safe for production — avoids overbroad matches
# =====================================================

# WordPress / common CMS probes
location = /wp-login.php                    { return 444; }
location = /wp-admin                        { return 444; }
location = /xmlrpc.php                      { return 444; }
location ~* ^/wordpress                     { return 444; }

# Known exploit file paths
location = /.env                            { return 444; }
location ~* \.(env|bak|old|sql|gz|zip|yml|yaml|json|log|pem|ini|cfg)$ { return 444; }

# Backup & config files by filename (not folders)
location ~* /(backup|config|settings|secrets|credentials|mailer|aws|sendinblue)[\-_]?(prod|dev)?\.(php|js|json|yml|yaml|env|log|sql|gz|zip)$ { return 444; }

# Suspicious tool routes
location = /phpinfo.php                     { return 444; }
location = /info.php                        { return 444; }
location = /debug.php                       { return 444; }
location = /server-status                   { return 444; }
location = /console.php                     { return 444; }

# Cloud provider metadata probe attempts
location ~* ^/169\.254\.169\.254            { return 444; }
location ~* ^/latest/meta-data              { return 444; }
location ~* ^/metadata/instance             { return 444; }

# Dangerous folders with sensitive files
location ~* ^/(admin|dev|test|tmp|private|core|debug|backup|backend|cloudfront|certs|aws_lambda)/.*\.(php|js|json|yml|yaml|env|log|sql|gz|zip)$ { return 444; }

# GraphQL & API gateway probes
location ~* ^/(graphql|swagger|openapi|api-gateway|actuator)/ { return 444; }

# Block sensitive .well-known abuse
location = /.well-known/aws.json            { return 444; }

# (Optional) Block all HEAD requests if bots abuse it — test first!
# if ($request_method = HEAD) {
#     return 444;
# }

# End of blocklist

Tip: 444 silently drops the connection — perfect for wasting bot cycles

How to Set It Up

  1. Create the snippet at /etc/nginx/snippets/blocked-routes.conf
  2. Paste in the rules from the Gist
  3. Include it in your NGINX config:
include snippets/blocked-routes.conf;

Test and reload:

sudo nginx -t && sudo systemctl reload nginx
Feature Cloudflare WAF Solution NGINX Blocklist Solution
Requires Cloudflare? ✅ Yes ❌ No
Protects at edge? ✅ Global via CDN ✅ Local server-level
Portability Moderate (requires Cloudflare setup) High – usable on any NGINX server
Customization Rich WAF & Bot Management UI Manual editing in configs
Free-tier viability WAF/Bot Mode available even free Requires only NGINX

Final Thoughts

  • If you're already using Cloudflare, leveraging its WAF and Bot Management is easy, powerful, and cloud-agnostic.
  • If you don’t want Cloudflare, this NGINX snippet gives you flexibility, control, and portability — with zero dependencies.

A big shout-out to Michael Dyrynda — his Cloudflare-based solution helped inform my implementation and confirmed that proactive edge blocking is the right approach.

Use It Yourself

Grab the NGINX blocklist from my Gist Fork it, tweak it, and reuse it across your infrastructure. Or go the Cloudflare route — whichever fits your stack best.

Laravel
Nightwatch
NGINX
DevOps
Monitoring
Performance
Steven Richardson
Steven is a software engineer with a passion for building scalable web applications. He enjoys sharing his knowledge through articles and tutorials.