Put your own check screen in front of your site
The check screen you know from the big CDNs, carrying your name instead of theirs and running on your own server. A visitor arrives, a one-second check runs, and they are through; an address on the shared threat list never reaches your application at all. No DNS change, no traffic routed through anyone else.
- Free
- MIT licence
- PHP 7.0+
- No dependencies
- Static sites too
Install it and your daily API quota goes to 30,000 lookups — nothing to apply for. Pricing →
What your visitor sees
There are three screens and all three carry your brand. The controls below feed the real template: the colour, name and language you pick render the page your visitor will actually get — not a screenshot of it, the running page.
Live — the proof of work really runs in your browser
How a request is decided
Top to bottom, first match wins. Each rung is cheaper than the one below it, so most decisions end in a single disk read.
- 01 Not a page request Assets, XHR, webhooks, wp-cron — untouched
- 02 Your own allow list Through
- 03 Your own deny list Refused
- 04 A trap path (/.env, /.git/…) Refused, and reported if you opted in
- 05 A verified search crawler Through — reverse DNS, forward-confirmed
- 06 Listed, or score ≥ block threshold The branded 403 page
- 07 Score ≥ verify threshold, or a flood Press and hold + proof of work (+ captcha)
- 08 Everyone else The one-second automatic check, then a signed cookie
POST requests are never interrupted — a form submission cannot survive an interstitial. A refused address is refused whatever the method.
Where the decision comes from
Two sources, either or both. In list mode — the default — nothing leaves your server while a visitor waits.
| List (feed) | API (/check) | Both | |
|---|---|---|---|
| Outbound calls per visit | 0 | 1 per address (cached) | only for addresses not on the list |
| Latency | binary search, microseconds | one round-trip on first sight | zero on most visits |
| Spends the daily quota | no | yes | capped by a budget |
| Freshness | refreshed every few hours | live | live |
| Anything leaves your server | no | the IP address only | only addresses not on the list |
| What you get back | score + category | score, country, network, label | both combined |
The list is capped by your tier: 5,000 addresses for a new account, 50,000 once your email is verified. It is written to disk as a sorted index — a lookup in a 39,208-address list is about 17 reads.
Install
Three minutes, and you can do it without touching a line of application code.
-
1
Get a free API key
https://ip-db.com/en/account/register -
2
Download the kit
cd /home/you/public_html curl -fsSL https://ip-db.com/shield/ipdb-shield.zip -o ipdb-shield.zip unzip ipdb-shield.zip && rm ipdb-shield.zip -
3
config.php
php ipdb-shield/ipdb-shield.php --secret php ipdb-shield/ipdb-shield.php --refresh php ipdb-shield/ipdb-shield.php --status -
4
Install
# either, as the first line your site runs: require __DIR__ . '/ipdb-shield/ipdb-shield.php'; # or, without touching any code, in .htaccess: php_value auto_prepend_file "/home/you/public_html/ipdb-shield/ipdb-shield.php"
-
1
wp-content/mu-plugins/
examples/wordpress-mu-plugin.php → wp-content/mu-plugins/ipdb-shield-loader.php -
2
config.php · skip_paths
'skip_paths' => array('/wp-cron.php', '/wp-json/**', '/xmlrpc.php', '/wp-admin/admin-ajax.php'), -
3
WordPress
The plugin runs inside WordPress and protects WordPress. The shield runs at the very start of PHP: the decision is made before WordPress loads at all, and it covers your non-WordPress files too. You can run both.
Integrations →
<script src="/ipdb-shield.js"
data-token="YOUR-SITE-TOKEN"
data-brand="Example"
data-accent="#2563eb"
data-block="75" data-verify="40"></script>
The honest limit: this edition runs in the visitor’s browser. It stops automated traffic that executes JavaScript and gives a human a clear explanation, but a crawler that ignores scripts still reads your HTML. Real enforcement happens server-side — if anything on your site is generated by PHP, use the PHP edition.
In the box
The whole kit is these files, and you can read every one of them here before you download anything.
Requirements
- PHP 7.0 or newer — shared hosting included
- cURL or allow_url_fopen, to fetch the list
- One writable directory (list, counters, log)
- A free IP-DB API key
- PHP-GD only if you want the image captcha
Verify what you downloaded
The zip is rebuilt from the source tree with fixed entry metadata: the same sources always produce the same bytes, and therefore the same digest.
764da798ab8deeb81c17002d2f9ac726ee43750049752329c0ad4bccc9980e4d
Build your configuration
These fields write config.php live. Your API key never leaves your browser — the file is assembled here, on your machine.
Your own panel
The kit derives a URL only you know. It shows the mode, the age and size of the local list, today’s counters, the last seven days and the most recent decisions with the reason for each. No login, no database — the URL comes from your secret.
$ php ipdb-shield.php --status
IP-DB Shield 1.0.0 · mode protect · source feed · PHP 8.2.12
blocklist: 39,208 entries, 42 min old
today: {"checked":7412,"blocked":1284,"solved":7390,"traps":38}
panel: https://example-shop.com/?ipdb_shield=status&t=…
$ php ipdb-shield.php --test 203.0.113.42
203.0.113.42 score=96 listed=yes category=ssh_brute_force
source=feed -> BLOCK
So it cannot go wrong
Your search rankings
Googlebot and friends are let through — but only when the address really belongs to them: the reverse DNS name must end in the operator’s domain and resolve back to the same address. A user agent claiming to be Googlebot proves nothing. The check screen answers 503 with noindex, never a 200 page carrying your title.
Failure leaves the site up
No list, an unreachable API, an unwritable cache directory: the visitor goes through and the reason lands in the PHP error log. A gate must never become an outage.
The address cannot be forged
REMOTE_ADDR is the only authority. A forwarded header is read only when the connection itself came from a proxy you listed.
One-shot challenges
Every check is signed, expires in ten minutes, is bound to the browser and can be solved once. A captured proof of work is worthless the second time.
Accessible
Keyboard-operable, announced to screen readers, respects reduced motion and dark mode, works at phone width. A visitor without JavaScript is told so in their own language instead of being left on a spinner.
Privacy
In list mode nothing about a visitor leaves your server. In API mode a single IP address is sent: no URL, no user agent, no cookie, no content. The screen tells the visitor which of the two is happening.
Questions
Will it slow my site down?
Will it hurt my Google rankings?
What if it blocks the wrong person?
I use Cloudflare. Does it conflict?
Is it free, or free for now?
How is this different from your WordPress plugin?
Goes well with
Five minutes from now your site has a doorman
Open a free account, copy your key, download the kit. There is no paid tier — not for business use either.