A few days ago I built Onionservatory, because apparently Onion Services also deserve to be judged by automated security tools.
It answers questions like:
1 | Is this Onion Service configured properly? |
Useful.
But after staring at the service side for long enough, another question appeared:
Fine. We know what the Onion Service is doing. What does the Onion Service know about us?
So naturally, instead of closing the terminal and doing something productive, I built another tool.
It is called Onionspector.

The other side of the connection
When you visit a website, the server does not just receive:
1 | GET / |
and politely mind its own business. Even without JavaScript, an HTTP request can reveal quite a lot about the browser making it.
Things such as:
1 | User-Agent |
may already tell the service something about your browser configuration. Enable JavaScript and the list gets significantly more interesting.
Now a page may inspect:
1 | screen dimensions |
None of these values necessarily identifies you by itself. That is not how browser fingerprinting usually becomes interesting.
The problem is the combination.
1 | browser |
and suddenly “just another visitor” may start looking rather less generic. Tor Browser spends a considerable amount of effort trying to prevent exactly that. Onionspector lets you see how well that effort is working from the perspective of an Onion Service.
Correlation is where it gets interesting
A single fingerprinting signal is usually not very exciting. Your timezone alone does not identify you. Neither does your screen size, Canvas behaviour, WebGL renderer or browser language. But if the same unusual combination starts appearing across several Onion Services, things become more interesting. Imagine one operator controls multiple sites in the Tor network and quietly collects browser-visible metadata from all of them. They may start seeing something like:
1 | same unusual viewport |
on:
1 | market A |
Individually, those signals may mean very little. Together, they may provide enough similarity to suggest:
This could be the same browser visiting all of these places.
Now add accounts. Maybe you register as random_darknet_username on one service, use the same or related identity somewhere else, log in repeatedly from a browser exposing a similar fingerprint and keep following the same browsing patterns. Suddenly the operator may not know who you are in the real world, but they may be able to build a reasonably useful picture of your anonymous identity:
1 | where you appear |
That is correlation. And correlation is often much more useful than discovering one magical value that says:
1 | HELLO, MY NAME IS BOB AND THIS IS MY HOME ADDRESS |
Large advertising and analytics companies have used variations of this idea for years on the clearnet: combine identifiers, browser characteristics, behaviour and metadata across many places to build increasingly detailed user profiles. Usually this is explained with words like:
1 | personalization |
which sounds much nicer than:
1 | we watched you move around the Internet and took notes |
The Tor network changes the transport and removes many conventional tracking opportunities, but browser-visible metadata can still matter if enough of it is exposed and somebody controls enough observation points. So the real question is not only:
Can this one Onion Service identify me?
A better question is:
If several services can observe the same unusual browser characteristics, how easily could those observations be correlated?
That is one of the reasons Tor Browser tries so hard to make users look alike. The boring browser is the useful browser. Somewhere, Mark Zuckerberg is probably still trying to understand why humans dislike being tracked. Give him time.
One button
I deliberately did not turn this into:
1 | Passive mode |
There is one test. You open Onionspector and get:
1 | Inspect your Tor Browser. |
Nothing happens before you click it. Once you do, Onionspector performs every check that your current browser configuration permits. First, the server-visible checks run. Then, if JavaScript is available, browser-side fingerprint checks continue automatically. If JavaScript is disabled, the test simply stops where JavaScript becomes necessary and tells you which additional checks could not be performed. No second test. No hidden “advanced” button. No requirement to enable JavaScript just to see the basic result.
What can be checked without JavaScript?
Quite a bit. The first stage uses only the HTTP request that reaches Onionspector. It looks at signals including:
1 | User-Agent |
This is useful because disabling JavaScript does not make the browser completely invisible. The server still has to receive an HTTP request. And HTTP requests have headers. Technology remains annoyingly functional. For example, a browser might send something roughly resembling:
1 | Mozilla/5.0 (...) Gecko/20100101 Firefox/140.0 |
That is useful information. But Onionspector intentionally does not say:
1 | THIS IS DEFINITELY TOR BROWSER |
because that would be nonsense. A normal Firefox instance can imitate some Tor Browser-visible properties. Privacy extensions can modify others. Custom configurations can produce strange combinations. So the result is deliberately expressed as something like:
1 | Tor Browser-like |
rather than pretending that an HTTP header is a cryptographic attestation of your browser installation.
Then JavaScript arrives
If JavaScript is allowed, Onionspector automatically continues with browser-side tests. This is where fingerprinting becomes much more interesting. The current checks include:
1 | navigator.userAgent |
then window and display information:
1 | viewport size |
then environment information:
1 | timezone |
and finally some of the usual fingerprinting favourites:
1 | Canvas |
Basically, a collection of things a normal site could inspect if you let it execute JavaScript. Which is exactly the point.
Letterboxing
Screen dimensions are one of the obvious browser fingerprinting signals. If everybody exposes their exact window size:
1 | 1537 x 947 |
that can become another useful attribute in a fingerprint. Tor Browser uses letterboxing to push users into standardized window-size buckets rather than exposing endlessly varying dimensions. Onionspector looks at the viewport and reports whether the dimensions resemble those expected buckets. That does not prove your browser is perfectly protected. But it gives you a useful indication of whether one of Tor Browser’s most visible anti-fingerprinting mechanisms appears to be doing its job. It may also remind you why maximizing a privacy browser window just because you paid for the entire monitor is not always the most anonymous lifestyle choice.
Canvas
Canvas fingerprinting is one of the classics. JavaScript draws something into an HTML canvas and reads the resulting pixels.
Differences in:
1 | operating system |
can influence the result.
So instead of asking:
What GPU do you have?
a site can sometimes ask your browser to draw something and let the pixels answer indirectly. Elegant. Slightly horrifying. Very web. Onionspector performs a local Canvas probe and reports what the browser allows it to observe. It is not trying to build a global fingerprint database. There is no server-side collection of Canvas hashes. The result stays in the browser.
WebGL
WebGL is another useful source of fingerprinting information. Depending on browser protections, JavaScript may be able to learn details about the rendering environment, including vendor and renderer information. That can expose surprisingly specific values on normal browsers. Onionspector checks:
1 | WebGL availability |
and shows what is actually visible.
Again, the interesting question is not:
Does WebGL exist?
The more useful question is:
How specific is the information my browser is willing to expose?
A privacy browser saying something generic is rather different from a normal browser cheerfully introducing your GPU by full name.
Fonts
Installed fonts are another classic fingerprinting signal. A very unusual collection of fonts can reveal information about:
1 | operating system |
and probably the fact that at some point you downloaded thirty-seven cyberpunk fonts and used exactly one of them. Tor Browser attempts to reduce font-based uniqueness. Onionspector performs a small local font sample to see what the browser allows the page to distinguish. It deliberately does not try to enumerate thousands of fonts. The idea is to inspect the protection, not recreate a 2014 advertising SDK.
AudioContext
Yes, audio can also be used for browser fingerprinting. No, the browser does not need to play death metal through your speakers. AudioContext can process an audio graph internally and numerical differences in the resulting signal can become another fingerprinting attribute. Onionspector performs a small local AudioContext probe when the API is available.
Again:
1 | nothing is uploaded |
It simply shows that this class of signal is available to the page.
WebRTC
WebRTC deserves special clarification because people understandably associate it with IP leaks. Onionspector does not perform an active WebRTC leak test. It does not:
1 | create a peer connection |
It only checks whether relevant WebRTC and media APIs are exposed to JavaScript. That is intentional.
I wanted a browser inspector, not a service that says:
Let me test whether you leak data by first contacting six unrelated servers.
Privacy engineering can become remarkably creative when left unsupervised.
No fingerprint submission
This is probably the most important implementation detail. The JavaScript-only results are not submitted to the server.
There is no:
1 | POST /fingerprint |
There is no fingerprint database. There is no account. There is no user history. There is no:
1 | Congratulations! |
The advanced fingerprinting code executes locally and renders its results locally. The Content-Security-Policy for the test page also includes:
1 | connect-src 'none' |
so the script itself cannot start using:
1 | fetch() |
to quietly send its findings somewhere else. A fingerprint privacy checker silently uploading your fingerprint would be a reasonably good joke. I just did not want to make it.
JavaScript disabled?
That is fine. If JavaScript is blocked, Onionspector still completes all server-visible checks. Then it tells you something along the lines of:
1 | Additional checks unavailable. |
That behaviour is important. Disabled JavaScript is not treated as:
1 | ERROR |
It means exactly what it means: Onionspector cannot observe JavaScript-only information because your browser did not permit JavaScript to observe it. Which, when testing browser privacy, is itself fairly useful information.
The Tor Browser reference
There is another problem with browser checkers. Browsers change. Tor Browser changes. Firefox ESR changes. A privacy test hardcoded against today’s browser profile eventually becomes tomorrow’s confidently incorrect browser profile. At the time I built the first version, the validated reference was:
1 | Tor Browser 15.0.x |
That reference will not remain current forever. So Onionspector has a small server-side reference checker. Once a week it asks the official Tor Browser release endpoint for the current stable release. The request itself is sent through Tor. At the time of writing it reports:
1 | Tor Browser stable: 15.0.21 |
The important part is what happens when this changes. Onionspector does not automatically rewrite its fingerprint heuristics. If Tor Browser eventually moves to another major profile, the reference becomes:
1 | Profile review required |
instead of blindly assuming that changing:
1 | 140 |
to:
1 | whatever-new-number |
is enough to understand a new browser release. Automation is useful. Automating incorrect assumptions is also useful, mainly if you enjoy debugging false positives.
Can it tell me that my Tor Browser is outdated?
Sometimes. But not with magical precision. Tor Browser deliberately normalizes browser-visible information. A website cannot necessarily distinguish:
1 | Tor Browser 15.0.20 |
from:
1 | Tor Browser 15.0.21 |
just by looking at the exposed browser fingerprint. And that is a feature.
What Onionspector can detect more reasonably is a profile that appears substantially older than the current reference.
For example:
1 | Current reference: |
may produce:
1 | WARN |
But if both releases expose the same ESR major, Onionspector does not pretend it knows the exact patch version. A privacy test should probably avoid defeating privacy protections just so it can print a more impressive version number.
What does a good result look like?
That is slightly different from Onionservatory. There is no:
1 | A+ |
and I deliberately like it that way. Onionservatory evaluates a service configuration, where scoring makes sense. Onionspector is looking at a collection of browser-visible signals.
The goal is not:
1 | Privacy: 97/100 |
because that number would mostly create an exciting illusion of mathematical certainty. Instead, the result tells you what is exposed, what appears standardized, what differs from the current Tor Browser-like profile, and which checks were unavailable.
You then get something much more useful:
1 | Tor Browser-like profile |
or perhaps:
1 | browser profile differs |
That tells you why something is interesting. Not just that an algorithm deducted seven privacy points because a spreadsheet said so.
It is not a deanonymization oracle
Another important distinction. Onionspector cannot tell you:
1 | YOU ARE ANONYMOUS |
It also cannot tell you:
1 | YOU HAVE BEEN DEANONYMIZED |
Browser fingerprinting is one component of privacy. There are many others:
1 | network behaviour |
A perfect-looking browser fingerprint does not save somebody who logs into the same personal account from Tor and the clearnet. Technology unfortunately still refuses to patch humans automatically.
Onionspector answers a much narrower question:
What browser-visible information can this Onion Service see right now?
That is enough.
Why another Onion Service?
Just like Onionservatory, Onionspector is onion-only. The service is specifically about Tor Browser privacy. Its intended users are already using Tor Browser. So the architecture is deliberately boring:
1 | Tor Browser |
There is also a tiny scheduled reference updater:
1 | systemd timer |
No database.
No accounts.
No analytics.
No fingerprint collection infrastructure.
No container orchestra slowly achieving consciousness.
It is a small tool. I would like it to remain one.
Onionservatory + Onionspector
The two projects now look at opposite ends of the same connection.
Onionservatory asks:
1 | What is this Onion Service exposing? |
Onionspector asks:
1 | What am I exposing to this Onion Service? |
Together they cover a surprisingly useful part of the interaction:
1 | Tor connection |
One looks inward. One looks outward. Neither launches nmap. I consider that personal growth.
Use it
Open Onionspector in Tor Browser and press:
1 | Test my browser |
Try it with your normal Tor Browser configuration. Then try different Tor Browser security levels. If you are curious, compare it with ordinary Firefox or Chromium routed through Tor and see how different the browser-visible surface becomes. Do not interpret every difference as an immediate vulnerability. The interesting part is understanding what a website can observe and which signals Tor Browser attempts to standardize or restrict. And if Onionspector tells you that your beautifully customized privacy browser exposes a unique combination of screen size, timezone, GPU, fonts and twenty APIs… well. At least the wallpaper is probably nice. I am interested in strange results, browser configurations, Tor Browser updates, false positives and fingerprinting edge cases that make the assumptions fall apart. That is usually where these tools become useful.
Oh, and here is the Onionspector URL:
1 | http://wcxzuepn4llq33dxino5iy3hwppjjpcrt5lvvydv6pvgnnobuyjoxmid.onion/ |