Scry: is that visitor even a browser?
Here's a real User-Agent string, the thing every browser sends with every request:
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0
That's Microsoft Edge. It claims to be Mozilla, says it's "like Gecko", mentions Safari and name-drops Chrome along the way. The only honest part is the Edg at the very end. Every browser does this, for reasons that go back to the 90s and involve a lot of websites checking for the wrong thing. The result is a string that's technically readable and practically useless.
I wanted my apps' login logs to say "Chrome on Windows" instead of that, and to notice when a "visitor" was actually curl or a script. That's all.
The obvious answer, and why I didn't take it
The well-known tool for this is matomo/device-detector, and it's excellent. It can tell you the exact browser version, the phone's make and model, and which of hundreds of crawlers just hit you. To do that it carries a big, constantly maintained database of patterns, because the world of User-Agents never stops changing.
For what I needed, that's a lot of library. I didn't want versions or phone models. I wanted four answers.
Four questions, and no more
Scry answers exactly these:
- Is this a real browser? Or is it
curl,wget, a Python script, Postman, headless Chrome, a crawler? - Which browser? Chrome, Firefox, Safari, Edge and friends.
- Which OS? Windows, macOS, iOS, Android, Linux and so on.
- Desktop, tablet or mobile?
And it's just as clear about what it won't do: no version numbers, no device models, no catalog of named bots. That's not laziness, it's the whole design. Every one of those is a permanent maintenance job, and saying no to them is what keeps Scry to one file you can read in a sitting. If you need that precision, the README sends you straight to device-detector.
use Scry\Scry;
$d = Scry::detect(); // reads the current request
if (!$d->isBrowser()) {
// curl, a script, a crawler...
}
echo $d->label(); // "Chrome on Windows"
The bits I'm quietly proud of
- It never guesses. Give it an empty or nonsense string and you get
unknownwith a confidence oflow, never a confident wrong answer. Andraw()always hands back the original string, so you never lose the truth to a label. - Client Hints first. Modern Chromium browsers send cleaner
Sec-CH-UAheaders alongside the User-Agent. Scry prefers those when they're there, and ignores the deliberately fake "GREASE" brand names browsers mix in to stop servers hard-coding things. (Yes, browsers lie on purpose now, to stop us relying on the lies they told before.) - The iPad problem. Since iPadOS 13, an iPad in Safari says it's a Mac. Scry's test corpus has that case in it, along with phones, tablets, bots, tools, empty strings and garbage.
- No dev dependencies for the tests either.
php tests/run.phpand you're done.
About the name
It started life as "Sift", until a quick search found a well-known company already using it. Scry stuck: to scry is to read the future in a crystal ball, which felt about right for a library that squints at a string of text and tells you what's on the other end.
Try it
- Composer:
composer require 1978io/scry(Packagist) - Or just copy the file.
src/Scry.phpis the whole library. Drop it in andrequireit. - Live demo: bits.1978.io/scry shows what it makes of your own browser.
- Code: github.com/1978io/scry. PHP 8.0+, MIT.
A JavaScript version for Node is on the someday list. If that would be useful to you, tell me and it moves up.