FingerprintJS collects browser signals to create unique visitor identifiers. This guide shows you how to bypass FingerprintJS detection using browser automation tools, fingerprint spoofing, and anti-detection techniques. You'll learn practical methods to evade fingerprinting for web scraping, privacy protection, and security research.
What is the Main Difference Between FingerprintJS and Other Fingerprinting Tools?
The main difference between FingerprintJS and other fingerprinting tools is its dual nature. The open-source version generates fingerprints entirely in the browser with 40-60% accuracy, while Fingerprint Pro (the commercial version) combines browser signals with server-side analysis, cookies, and behavioral tracking to achieve 99.5% accuracy. This makes the Pro version significantly harder to bypass than pure JavaScript fingerprinting solutions like CreepJS or Broprint.js.
Understanding FingerprintJS Architecture
FingerprintJS comes in two versions.
The open-source version runs entirely in the browser. It collects signals like canvas fingerprints, WebGL parameters, audio context, and navigator properties. These signals get hashed into a visitor identifier.
Fingerprint Pro adds server-side intelligence. It tracks users across incognito mode, combines multiple identification methods, and builds behavioral profiles. This version integrates cookies, IP analysis, and machine learning.
For web scrapers, this distinction matters. You can bypass the open-source version with standard evasion techniques. The Pro version requires more sophisticated approaches.
Why Bypass FingerprintJS?
Legitimate use cases exist for bypassing browser fingerprinting.
Privacy protection: Users concerned about tracking want to prevent persistent identification. Fingerprinting bypasses cookie controls and operates without consent.
Web scraping research: Developers building scrapers need to test against fingerprinting defenses. Understanding bypass techniques helps create robust data collection tools.
Security testing: Companies implementing FingerprintJS need to test their defenses. Penetration testers must verify fingerprinting can't be easily circumvented.
Academic research: Researchers studying online privacy analyze fingerprinting effectiveness. Testing bypass methods reveals privacy vulnerabilities.
This guide focuses on educational and research purposes. Always respect websites' terms of service.
Detection Signals FingerprintJS Collects
FingerprintJS gathers dozens of browser signals.
Canvas fingerprinting renders text and shapes then extracts pixel data. Different GPUs, drivers, and font rendering create unique patterns. Even identical browser versions produce different hashes.
WebGL fingerprinting queries graphics card capabilities. GPU vendor, renderer string, supported extensions, and rendering outputs vary between systems. This alone provides high entropy.
Audio context fingerprinting generates audio signals and measures processing. Different hardware processes audio uniquely. The Web Audio API exposes these differences.
Navigator properties include user agent, language, timezone, plugins, and platform. While easily spoofed individually, inconsistencies between properties raise flags.
Screen metrics capture resolution, color depth, and available space. Touch support and pixel ratio add more signals.
Font detection identifies installed fonts. Websites can test thousands of fonts to build unique profiles. Enterprise systems with custom fonts become highly identifiable.
Browser features like WebRTC, Battery API, and hardware concurrency provide additional entropy. The combination creates strong fingerprints.
Method 1: Spoofing Browser Automation Tools
Default Playwright, Selenium, and Puppeteer leak obvious automation signals.
The navigator.webdriver property returns true in automated browsers. FingerprintJS checks this immediately.
Here's how to hide it in different tools.
Playwright Bypass:
from playwright.sync_api import sync_playwright
def bypass_webdriver():
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
context = browser.new_context()
# Remove webdriver property
context.add_init_script("""
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined
})
""")
page = context.new_page()
page.goto('https://fingerprintjs.github.io/fingerprintjs/')
# Additional spoofing
page.evaluate("""
Object.defineProperty(navigator, 'plugins', {
get: () => [1, 2, 3, 4, 5]
})
""")
browser.close()
This script masks the webdriver property before page load. FingerprintJS can't detect it through standard checks.
Selenium Bypass:
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
options = Options()
options.add_argument('--disable-blink-features=AutomationControlled')
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option('useAutomationExtension', False)
driver = webdriver.Chrome(options=options)
# Override navigator properties
driver.execute_script("""
Object.defineProperty(navigator, 'webdriver', {
get: () => false
})
""")
driver.get('https://fingerprintjs.github.io/fingerprintjs/')
The --disable-blink-features flag removes automation indicators. Selenium becomes less detectable.
Puppeteer Bypass:
const puppeteer = require('puppeteer');
async function bypassDetection() {
const browser = await puppeteer.launch({
headless: true,
args: ['--disable-blink-features=AutomationControlled']
});
const page = await browser.newPage();
// Remove webdriver traces
await page.evaluateOnNewDocument(() => {
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined
});
// Spoof plugins
Object.defineProperty(navigator, 'plugins', {
get: () => [1, 2, 3]
});
});
await page.goto('https://fingerprintjs.github.io/fingerprintjs/');
await browser.close();
}
bypassDetection();
These basic spoofs handle simple FingerprintJS implementations. More sophisticated setups require additional layers.
Method 2: Using Stealth Plugins
Community-maintained stealth plugins patch dozens of detection vectors.
Playwright Extra with Stealth:
from playwright_stealth import stealth_sync
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page()
# Apply stealth patches
stealth_sync(page)
page.goto('https://fingerprintjs.github.io/fingerprintjs/')
# Check fingerprint
visitor_id = page.evaluate('FingerprintJS.load().then(fp => fp.get()).then(result => result.visitorId)')
print(f'Visitor ID: {visitor_id}')
browser.close()
Stealth plugins modify over 50 properties. They fix permissions, plugins, WebGL, canvas, and more.
Undetected ChromeDriver for Selenium:
import undetected_chromedriver as uc
options = uc.ChromeOptions()
options.add_argument('--headless=new')
driver = uc.Chrome(options=options)
driver.get('https://fingerprintjs.github.io/fingerprintjs/')
# Extract fingerprint
fingerprint = driver.execute_script("""
return FingerprintJS.load()
.then(fp => fp.get())
.then(result => result.visitorId);
""")
print(f'Fingerprint: {fingerprint}')
driver.quit()
Undetected ChromeDriver automatically patches Selenium. It updates regularly as detection methods evolve.
Puppeteer Extra Stealth:
const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
puppeteer.use(StealthPlugin());
(async () => {
const browser = await puppeteer.launch({
headless: true
});
const page = await browser.newPage();
await page.goto('https://fingerprintjs.github.io/fingerprintjs/');
// Get fingerprint
const visitorId = await page.evaluate(async () => {
const fp = await FingerprintJS.load();
const result = await fp.get();
return result.visitorId;
});
console.log(`Visitor ID: ${visitorId}`);
await browser.close();
})();
These plugins reduce detection significantly. They handle most FingerprintJS deployments on e-commerce and content sites.
Method 3: Fingerprint Rotation
Consistent fingerprints enable tracking. Rotating fingerprints prevents long-term identification.
Generate realistic fingerprint profiles:
import random
def generate_fingerprint_profile():
"""Creates believable browser fingerprint"""
user_agents = [
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36'
]
screen_resolutions = [
{'width': 1920, 'height': 1080},
{'width': 2560, 'height': 1440},
{'width': 1366, 'height': 768}
]
timezones = ['America/New_York', 'America/Los_Angeles', 'Europe/London']
languages = ['en-US', 'en-GB', 'en-CA']
return {
'user_agent': random.choice(user_agents),
'screen': random.choice(screen_resolutions),
'timezone': random.choice(timezones),
'language': random.choice(languages),
'hardware_concurrency': random.choice([2, 4, 8, 16])
}
This function creates realistic fingerprint combinations. Apply it when launching browsers.
Implement rotation with Playwright:
from playwright.sync_api import sync_playwright
def scrape_with_rotation():
profile = generate_fingerprint_profile()
with sync_playwright() as p:
browser = p.chromium.launch()
context = browser.new_context(
user_agent=profile['user_agent'],
viewport=profile['screen'],
locale=profile['language'],
timezone_id=profile['timezone']
)
page = context.new_page()
# Override hardware concurrency
page.add_init_script(f"""
Object.defineProperty(navigator, 'hardwareConcurrency', {{
get: () => {profile['hardware_concurrency']}
}})
""")
page.goto('https://example.com')
browser.close()
Each scraping session gets a unique fingerprint. FingerprintJS sees different visitors.
Critical: Maintain consistency within a session. Don't change fingerprints mid-browse. This creates obvious inconsistencies that trigger detection.
Method 4: Virtual Display Technique
Headless browsers have different fingerprints than regular browsers.
FingerprintJS detects headless Chrome through rendering inconsistencies. WebGL behaves differently. Canvas rendering produces distinct outputs. Audio context reveals headless state.
Running headful browsers on virtual displays solves this.
Install Xvfb on Linux:
sudo apt install xvfb
Use virtual display with Python:
from pyvirtualdisplay import Display
from selenium import webdriver
# Start virtual display
display = Display(visible=0, size=(1920, 1080))
display.start()
# Launch headful browser
driver = webdriver.Chrome()
driver.get('https://fingerprintjs.github.io/fingerprintjs/')
# Scrape as needed
fingerprint = driver.execute_script("""
return FingerprintJS.load()
.then(fp => fp.get())
.then(result => result.visitorId);
""")
print(f'Generated fingerprint: {fingerprint}')
driver.quit()
display.stop()
The browser runs in headful mode. FingerprintJS sees a normal browser environment.
This technique works on servers without monitors. It's resource-intensive but highly effective.
Method 5: Using Anti-Detect Browsers
Specialized browsers bypass fingerprinting by design.
Camoufox is a modified Firefox build. It spoofs canvas, WebGL, and audio fingerprints. Each session generates unique but realistic fingerprints.
from camoufox.sync_api import Camoufox
with Camoufox(headless=True) as browser:
page = browser.new_page()
page.goto('https://fingerprintjs.github.io/fingerprintjs/')
# Camoufox automatically spoofs fingerprints
visitor_id = page.evaluate("""
() => FingerprintJS.load()
.then(fp => fp.get())
.then(result => result.visitorId)
""")
print(f'Camoufox fingerprint: {visitor_id}')
Multilogin and GoLogin are commercial anti-detect browsers. They maintain fingerprint profiles and handle canvas, fonts, and WebGL automatically.
These tools cost money but save development time. They're updated as fingerprinting evolves.
Bypassing Fingerprint Pro (Commercial Version)
Fingerprint Pro uses server-side intelligence. Browser-only bypasses fail.
Challenges with Pro:
It correlates multiple visits. If you spoof fingerprints but keep the same IP, it tracks you. Behavioral patterns persist across sessions.
It uses cookies for confirmed visitors. Once you've been cookied, fingerprint changes alone won't help. You need to clear cookies AND change fingerprints.
It detects impossible configurations. Mismatched GPU and OS signals get flagged. Windows reporting Mac GPU models triggers suspicion.
Strategies for Pro:
Combine residential proxies with fingerprint rotation. Different IPs per session prevent correlation. Residential IPs look like real users.
import requests
from playwright.sync_api import sync_playwright
PROXY_URL = 'http://username:password@residential-proxy.com:8080'
def scrape_with_proxy():
profile = generate_fingerprint_profile()
with sync_playwright() as p:
browser = p.chromium.launch(
proxy={'server': PROXY_URL}
)
context = browser.new_context(
user_agent=profile['user_agent'],
viewport=profile['screen']
)
page = context.new_page()
page.goto('https://example.com')
browser.close()
Limit request frequency. Scraping 100 pages per second from one visitor ID looks suspicious. Space requests naturally.
Clear all storage between sessions. Cookies, localStorage, and IndexedDB can leak across fingerprints.
Testing Your Bypass
Verify your bypass works before production scraping.
Test against official demo:
Visit https://fingerprintjs.github.io/fingerprintjs/ with your automated browser. Check if the visitor ID changes between sessions. If it stays constant, your fingerprint isn't randomizing.
Compare fingerprints:
Run your scraper multiple times. Extract the fingerprint each time. Unique fingerprints indicate successful randomization.
def test_fingerprint_bypass():
fingerprints = []
for i in range(5):
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto('https://fingerprintjs.github.io/fingerprintjs/')
fp = page.evaluate("""
() => FingerprintJS.load()
.then(fp => fp.get())
.then(result => result.visitorId)
""")
fingerprints.append(fp)
browser.close()
unique_count = len(set(fingerprints))
print(f'Generated {unique_count} unique fingerprints out of 5 attempts')
if unique_count == 5:
print('✓ Bypass successful - all fingerprints unique')
else:
print('✗ Bypass failed - fingerprints repeating')
Run this test after implementing bypass methods.
Check CreepJS scores:
Visit https://abrahamjuliot.github.io/creepjs/ with your scraper. Check the "Headless" and "Stealth" scores. Zero percent indicates good bypass. High scores mean detection vulnerabilities remain.
Common Pitfalls to Avoid
Inconsistent fingerprints within a session trigger detection. Don't change user agent mid-browse while keeping the same canvas fingerprint. Maintain internal consistency.
Ignoring behavioral signals causes flags. Instant page navigation, no mouse movement, and perfectly linear scrolling look robotic. Add random delays and simulate mouse paths.
Using datacenter IPs with residential fingerprints creates mismatches. Budget residential proxies if testing Pro deployments.
Not clearing storage between sessions leaks across fingerprints. localStorage values persist. Cookies connect sessions. Clear everything.
Forgetting canvas fingerprinting matters most. Many bypass attempts fix WebGL but ignore canvas. FingerprintJS relies heavily on canvas. Spoof it properly.
Testing only once misses intermittent detection. Test bypass 20+ times. Statistical patterns reveal weaknesses.
Conclusion
Bypassing FingerprintJS requires understanding which version you're facing.
The open-source version yields to standard stealth plugins and fingerprint spoofing. Playwright Extra, Puppeteer Stealth, and undetected ChromeDriver handle most cases.
Fingerprint Pro demands more sophisticated approaches. Combine residential proxies, behavioral emulation, and canvas spoofing. Or use commercial scraping APIs.
For privacy research, virtual displays with headful browsers provide realistic fingerprints. For production scraping, fingerprint rotation prevents tracking.
Test thoroughly before deploying. FingerprintJS updates regularly as bypass techniques emerge.
FAQ
Does disabling JavaScript prevent FingerprintJS?
No. While FingerprintJS relies on JavaScript, websites can implement server-side fingerprinting using TLS parameters, HTTP headers, and TCP/IP characteristics. Disabling JavaScript also breaks most modern websites.
Can VPNs bypass FingerprintJS?
VPNs change your IP but don't affect browser fingerprints. FingerprintJS tracks you through canvas, WebGL, and fonts regardless of IP. Combine VPNs with fingerprint spoofing for effective bypass.
How often should I rotate fingerprints?
Rotate fingerprints between scraping sessions, not during sessions. For light scraping, rotate daily. For intensive scraping, rotate per session. Frequent rotation within sessions looks suspicious.
Will fingerprint spoofing get me banned?
Poor spoofing raises flags. Realistic fingerprints with consistent signals rarely trigger bans. Datacenter IPs with residential browser fingerprints get caught. Match your entire stack properly.