Most "block bad traffic" advice starts and ends with IP blocklists. That's a start, but IP-based rules break down fast in practice: attackers rotate through residential proxies, legitimate users share IPs behind NAT/CGNAT, and a single blocked IP can be one bad actor or a whole office building.
A layer that gets skipped way more often than it should: matching on the request itself — a header, a query parameter, or a cookie — instead of (or alongside) the source IP. Here's when each actually earns its place, with working examples for nginx, Apache, and Express.
1. Gate an internal/staging endpoint with a shared-secret header
If you have an endpoint that should only ever be hit by your own tooling — a webhook receiver, an internal admin API, a staging environment — don't rely on obscurity or IP allowlisting alone (IPs change, CI runners are ephemeral). Require a header instead:
# nginx
location /internal/ {
if ($http_x_internal_token != "your-secret-value") {
return 403;
}
}
# Apache (mod_rewrite)
<Location /internal/>
RewriteEngine On
RewriteCond %{HTTP:X-Internal-Token} !^your-secret-value$
RewriteRule ^ - [F]
</Location>
// Express middleware
app.use('/internal', (req, res, next) => {
if (req.headers['x-internal-token'] !== process.env.INTERNAL_TOKEN) {
return res.status(403).send('Forbidden');
}
next();
});
This is cheap, doesn't care what IP the request comes from, and rotates trivially if it ever leaks (just change the env var).
2. Block obvious bot/scraper traffic by User-Agent or missing headers
A shocking amount of low-effort scraping traffic doesn't bother sending a realistic User-Agent, or omits Accept-Language entirely — headers a real browser always sends. You can catch a meaningful chunk of junk traffic just by requiring these to be present, before it ever reaches your app logic:
# nginx
if ($http_user_agent = "") {
return 403;
}
# Apache (mod_rewrite)
RewriteCond %{HTTP_USER_AGENT} ^-?$
RewriteRule ^ - [F]
This won't stop a sophisticated attacker (they'll just fake a normal browser UA), but it's nearly free and kills a surprising amount of naive automated traffic.
3. Feature-flag or A/B test at the edge with query params or cookies
Not security-specific, but the same matching mechanism: routing based on a query param (?preview=true) or a cookie your app sets is how a lot of preview/canary deployments work without needing a separate hostname.
# nginx
if ($arg_preview = "true") {
proxy_pass http://staging-backend;
}
# Apache (mod_rewrite + mod_proxy)
RewriteCond %{QUERY_STRING} preview=true
RewriteRule ^(.*)$ http://staging-backend%{REQUEST_URI} [P,L]
The catch
None of this is exotic — the trade-off is that hand-maintained RewriteCond/if blocks scattered across config files get messy fast, and a typo in a security-relevant rule is exactly the kind of thing that silently fails open instead of loud. Worth testing each rule against both a request that should pass and one that shouldn't, not just the happy path.
(I work on a managed WAF, ShieldIngress — this is one of the things it does automatically, but everything above works with whatever you're already running.)
Top comments (0)