One line of HTML turns bot traffic, scrapers and click fraud into something you can see in a dashboard and redirect away from your pages.
Paste this just before the closing </head> tag, on every page or in your master template.
<script defer
data-public-key="YOUR_PUBLIC_KEY"
data-blocked-page="https://example.com/blocked"
src="https://cdn.jsdelivr.net/gh/moonito-net/lib@2.0.0/all.min.js"
integrity="sha384-VZ68WATj2o5YUO8bZ1L84gaexLLf6PqpDBBNPLJfgPNN5KUjd+M7APWoGiulEH91"
crossorigin="anonymous"></script>That is the whole installation.
Two things, from one tag.
It filters. Every page view is checked against the rules you set in your dashboard. Traffic you did not want is sent to data-blocked-page instead of your page. The decision is made on Moonito's side, so changing a rule takes effect immediately across every site you have installed it on, with nothing to redeploy.
It reports what the browser is. Screen size, timezone, language, whether the page was interacted with, and the signals that separate a real browser from an automated one. This half never blocks, never redirects and never decides anything; it is what makes the filtering half accurate enough to trust, and it is what lets Moonito spot a residential or mobile proxy pool by how a visitor behaves across requests rather than by the address they arrive from.
It does not read page content, form fields, keystrokes, or anything your visitor types.
| File | Contains |
|---|---|
all.min.js |
Filtering and sensor. Use this one. |
analytics.min.js |
Filtering only, for sites that already load the sensor separately |
Both take the same attributes. all.min.js is the two concatenated: the filtering half is byte for byte the code published as 1.0.0, so moving to it changes nothing about how filtering behaves.
<!-- filtering only -->
<script defer
data-public-key="YOUR_PUBLIC_KEY"
data-blocked-page="https://example.com/blocked"
src="https://cdn.jsdelivr.net/gh/moonito-net/lib@1.0.0/analytics.min.js"
integrity="sha384-8V4yTj3zJE7RRSGrVcibSdliQqhqE/kUEV26p7kxcoNG5fSefJWiS+SGxjjmR1yJ"
crossorigin="anonymous"></script>| Attribute | Required | Description |
|---|---|---|
data-public-key |
yes | From your Moonito dashboard. Safe to expose: it identifies the site, it does not grant access to it. |
data-blocked-page |
yes | Where filtered visitors are sent. Any absolute URL. |
defer |
recommended | Keeps the script from blocking page rendering. |
integrity + crossorigin |
recommended | Only valid on a pinned @version URL. |
data-consent="required" |
optional | Collects nothing that identifies a browser until you call moonito.consent(). |
data-spa="1" |
optional | Reports again on each route change in a single page app. |
@2.0.0 points at one exact commit, served as immutable, so the browser fetches it once and reuses it for a year. The integrity hash means the browser refuses to run the file if a single byte differs from what is published here, which is what you want from a script that is allowed to redirect your visitors.
An unversioned path is cached in your visitors' browsers for seven days and nothing can clear it early. If a bad build ever shipped there it would stay on their machines for a week. A pinned version cannot do that to you. The trade is that updates are not automatic: when a new version is published we will tell you, and updating means changing one number.
The unpinned path keeps working exactly as before and is not going away. There is nothing you need to change.
https://cdn.jsdelivr.net/gh/moonito-net/lib/analytics.min.js
This runs in the browser, so your page has already loaded by the time an unwanted visitor is sent away, and a client that never runs JavaScript never runs it at all. That is fine for analytics and for moving unwanted traffic along.
When what you are protecting costs you money, a checkout, a signup form, a paid campaign landing page, an API, put the decision on your server where it runs before a single byte of your page is sent:
- PHP SDK, also on Packagist as
moonito/php-sdk - Node and TypeScript
- Python
- Go
- WordPress plugin, from your dashboard
Running both is common and supported. The browser tag gives you the analytics, the server side check gives you the enforcement.
Does this slow my site down? No. It is deferred, so it loads alongside your page rather than in front of it, and a pinned version is served from cache on repeat visits.
Does it work in a single page app?
Yes. Add data-spa="1" and it reports on each route change.
What happens if the CDN is unreachable? Your site loads and works normally. It fails open by design: it will never be the reason a real visitor cannot read your page.
Is my public key a secret? No. It identifies your site so Moonito knows which rules to apply. It does not grant access to your account or your data.
| Version | Notes |
|---|---|
| 2.0.0 | Adds all.min.js: filtering and sensor in one tag. analytics.min.js unchanged. |
| 1.0.0 | First tagged release of analytics.min.js. |
- Website: moonito.net
- Guides: moonito.net/usage-guides
- Issues: github.com/moonito-net/lib/issues
MIT, (c) 2025 Moonito
Moonito. Stop bad bots. Start accurate web analytics.