CreepJS is an open-source test page for browser fingerprints. It reads what your browser reveals, from the screen and the fonts to the time zone and WebRTC, and turns it into one fingerprint ID. The project states its aim in one line: "The purpose of this project is to shed light on weaknesses and privacy leaks among modern anti-fingerprinting extensions and browsers."
We ran CreepJS 14 times on 11 October 2026, direct and through a proxy, in six setups. The proxy did not change the fingerprint ID. The time zone did. WebRTC showed our server's own address until a Chrome setting closed that route.
Which CreepJS is the real one?
The only official page is abrahamjuliot.github.io/creepjs, on GitHub Pages. The project is blunt about copies: "Any .org, .com, or custom domain claiming to be CreepJS is an unauthorized mirror and should be treated as a malicious honeypot designed to steal your fingerprint data."
The code is open under the MIT license, in the CreepJS repository on GitHub. Its author also draws a line. The goal is "to conduct research and provide education, not to create a fingerprinting library".
What does the CreepJS report show?
The top of the page shows two hashes. The FP ID is the fingerprint. Next to it sits a second hash, labelled Fuzzy. Below them, section follows section, each with a short hash of its own. Our runs recorded up to 21 of them, among them WebRTC, Timezone, Intl, Headless, Worker, WebGL, Screen, Fonts and Audio.
There is no score. Older guides build their advice on a trust score with grades. The page we loaded 14 times showed no trust score and no score of any kind. The only percentages left are the three in the Headless section.
What is the FP ID made of?
CreepJS builds the FP ID from what its code calls the stable fingerprint. The list is in the source:
- navigator values, such as the platform, the number of cores and the device memory
- the screen's width, height and colour depth
- values read in a background worker, among them the language, the time zone and the graphics card
- canvas and WebGL drawings, CSS media queries and fonts
- the time zone and an audio test
Two things are missing from that list. The IP address is not in it, and neither is the WebRTC section. CreepJS fetches the WebRTC data on its own, after the FP ID is computed.
One more rule matters if you fake values. When CreepJS finds that a value was faked, which it calls a lie, it leaves that part out of the FP ID. A faked screen size never enters the ID. The ID is then computed as if the screen were not there.
What happened in our test?
We ran the full Chrome 143 build in headless mode on our own server, with a fresh profile for every run. Each setup changed one thing:
- A: direct, four runs, the last one 31 minutes after the first.
- B: through a free elite proxy in Frankfurt from our own list.
- C: the same proxy, with Chrome's time zone set to America/Chicago.
- D: the same proxy, with a command-line flag that should block WebRTC's direct route.
- E: the same proxy, with a Chrome privacy setting for the same job.
- F: direct, with a 1280 by 720 window instead of 1366 by 900.
We masked our addresses before anything was saved.

Twelve runs gave the same FP ID. The two with a US time zone gave another one. The headless percentages never moved: 50% like headless, 100% headless and 0% stealth, in every run.
Why did the proxy not change the FP ID?
A proxy carries your traffic. It does not touch what your browser reports about itself. The screen, the fonts, the drawings and the time zone all come from your own machine. Since the IP address is not part of the FP ID, the proxy had nothing to change.
The time zone is a different case. A browser reports the time zone of the system it runs on. In JavaScript the value "is an IANA time zone name". MDN adds: "The default is the runtime's default time zone." Our Chicago setting changed three sections, Timezone, Intl and Worker, and with them the FP ID.
This is the part a proxy user has to watch. Your exit can sit in Germany while your browser reports Chicago. Test pages flag that mismatch, as we measured in BrowserScan explained. Match the time zone to the exit, and expect a new FP ID when you do.
Does the window size change the FP ID?
People ask whether a new viewport in Puppeteer changes the CreepJS fingerprint. In our runs it did not. A 1280 by 720 window kept the same FP ID as 1366 by 900.
The reason is in the code. The FP ID uses the screen's width and height, not the window's. Our headless Chrome reported an 800 by 600 screen whatever the window size, so nothing in the ID moved. A tool that also changes the screen size the page reads will change the ID.
Why does CreepJS show your real IP through a proxy?
The WebRTC section lists the addresses your browser's WebRTC stack finds. WebRTC carries calls and video in the browser. To find a route, it asks a STUN server which public address it sees. RFC 8828, the standard for WebRTC address handling, names the risk for proxy users: "WebRTC's STUN checks will bypass the proxy and reveal the public IP address of the client", unless the direct path is blocked.
We saw exactly that. Through the German proxy, the WebRTC section still showed our server's own address, found through STUN. The flag --force-webrtc-ip-handling-policy=disable_non_proxied_udp did not change that in the full Chrome build. A ten-line extension of our own did. It sets chrome.privacy.network.webRTCIPHandlingPolicy to disable_non_proxied_udp, a setting extensions can use since Chrome 48. With it on, the section showed no public address at all.
Chrome's policy list explains the value: "WebRTC uses either UDP SOCKS proxying or will fallback to TCP proxying". We compared the three ways to set it in our BrowserScan runs. A SOCKS5 proxy that carries UDP is covered in SOCKS5 UDP explained.
A proxy changes your address and leaves your fingerprint as it is. Pick an exit in the country your time zone names, and both tell the same story. Our residential proxies let you choose that country.
What do like headless, headless and stealth mean?
The Headless section shows three percentages. Each one is the share of a group of yes or no checks that came back true. Click the short code next to a percentage, and the page lists every check with true or false.
| Group | Checks | What they look for | Our result |
|---|---|---|---|
| Like headless | 16 | Signs that look like headless Chrome: no plugins, no PDF viewer, a software graphics card, missing browser features | 50%, 8 of 16 |
| Headless | 3 | The webdriver flag, and "HeadlessChrome" in the user agent of the page and of a worker | 100%, 3 of 3 |
| Stealth | 5 | Traces of tools that patch the browser to hide automation | 0%, 0 of 5 |
In our runs, eight like-headless checks came back true. They were a light colour scheme, a known background colour for ActiveText, no taskbar, the SwiftShader software renderer, and no Web Share, Content Index, Contacts Manager or downlinkMax. Some of these are true in an ordinary browser too. A light colour scheme is simply light mode. A like-headless share above zero proves nothing on its own.
The headless group is stricter, and all three of its checks were true for us. One is navigator.webdriver, which MDN says "indicates whether the user agent is controlled by automation". MDN also lists when Chrome sets it: "The --enable-automation or --headless flag is used". Deleting the property does not help. In a current browser, CreepJS counts a missing webdriver value as on, and one it finds faked as well. The other two checks look for "HeadlessChrome" in the user agent of the page and of a background worker.
The stealth group looks for the tools that try to hide all this. It asks five questions. Was the toString function patched? Is chrome.runtime a fake? Was the chrome object added late? Does an iframe have a window before it is on the page? Do the page and a worker report different graphics cards? We ran plain Chrome, so none of the five fired. Stealth plugins work on these browser signals and leave the network alone, as we show in proxies for puppeteer-extra.
None of these checks looks at the network. The three percentages were the same in all 14 runs, direct or through the proxy.
How do you read your own report?
- Open the official page in the browser profile you want to check.
- Note the FP ID and run the page again. In our runs the ID stayed the same across fresh profiles.
- Look at the WebRTC section. If it shows your own address while you use a proxy, close that route with Chrome's WebRTC policy, then check again.
- Compare the Timezone section with the country of your exit.
- Open the lists behind the three headless percentages and see which checks fired.
CreepJS shows what a page can read. It does not show what a given site does with it. Real sites combine many signals, as we cover in how websites detect proxies.
What this page could not check
- All runs used headless Chrome on a Linux server, so CreepJS saw a headless browser every time. A desktop browser shows other hardware, fonts and screen values, and other headless percentages.
- Our headless Chrome reported an 800 by 600 screen whatever the window size. The window test shows only that the window alone does not move the ID.
- We tested one free proxy, an elite HTTP proxy in Frankfurt, with two to four runs per setup.
- We did not test antidetect browsers or automation tools, or the enterprise policy route for WebRTC.
- The page does not say what the FP ID is made of. That comes from the source code at one commit, of 11 June 2026, and our runs agree with it.
- Chrome's headless shell lost the CreepJS tab within 5 seconds in four flag sets, so every run used the full Chrome build.
- CreepJS and Chrome change their checks over time. We will run the six setups again by 11 January 2027.
Sources
- CreepJS, README and source code (src/creep.ts, src/headless/index.ts) at commit 10aa6724cd of 11 June 2026, read 11 October 2026: github.com.
- CreepJS, the official test page, loaded 11 October 2026: abrahamjuliot.github.io.
- RFC 8828, WebRTC IP Address Handling Requirements, IETF, January 2021: rfc-editor.org.
- Chrome for Developers, chrome.privacy API, webRTCIPHandlingPolicy, read 11 October 2026: developer.chrome.com.
- Chrome Enterprise policy list, WebRtcIPHandling, read 11 October 2026: chromeenterprise.google.
- MDN Web Docs, Navigator.webdriver, read 11 October 2026: developer.mozilla.org.
- MDN Web Docs, Intl.DateTimeFormat resolvedOptions, read 11 October 2026: developer.mozilla.org.
- HProxy, free proxy list API, read 11 October 2026: hproxy.com/docs/free-proxy-list.
- Our own test: 14 runs of headless Chrome 143 on our server, 11 October 2026, read between 05:49 and 06:21 UTC.



