Bypass FingerprintJS

How to Bypass FingerprintJS in 2026: 5 methods

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-detect browsers. 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?

FingerprintJS is two separate products that share a name, and they need different bypass approaches. The free open-source library generates a fingerprint entirely in the browser with 40-60% accuracy. Fingerprint Pro, the commercial version, combines browser signals with server-side analysis, cookies, and behavioral tracking to reach 99.5% accuracy. Most other tools, including any open-source fingerprintjs alternative such as CreepJS, Broprint.js, or ThumbmarkJS, work like the free library: pure JavaScript running client-side.

The open-source library and its alternatives

The free FingerprintJS library is MIT licensed and runs inside the site's own code. It reads a few dozen browser settings and turns them into a hash. Because the hash is deterministic, changing a single signal produces a different hash, and the site sees a new visitor. Spoofing or randomizing browser attributes therefore works against this library and against similar client-side tools. ThumbmarkJS, for example, reports about 80% uniqueness across general browser populations. That is higher than the FingerprintJS library, but a spoofed signal changes its hash in exactly the same way.

Raw fingerprints also collide. Kaidn measured 2.31 people sharing one iPhone fingerprint, so a site relying only on the free library cannot reliably separate users on common devices.

Fingerprint Pro

Fingerprint Pro runs on the vendor's servers. It cross-checks browser signals against cookies, server-side analysis, and behavior. Spoofing one attribute is less effective here because the other signals can still link your sessions together.

Why Pro is easier to block

The Pro script loads from vendor domains that ad blockers already recognize. In Kaidn's testing, the Pro script failed to run in 21 of 24 sessions that used privacy tools. The self-hosted open-source library kept running in those sessions because it ships inside the site's own code, so blocking a vendor domain does nothing to it.

Each version therefore has a different weak point. Pro is hard to fool once it runs but easy to stop with content blocking. The open-source library is hard to block but easy to fool by changing what it reads.

Open-source FingerprintJS, CreepJS, Broprint.js, ThumbmarkJS: Spoof or randomize browser signals
Fingerprint Pro: Block the script with a content blocker, or match signals across every layer it checks

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?

There are legitimate reasons to bypass 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 reliable 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. Each is easy to spoof individually, but 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 most detection signals. 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: Run a headful browser on a virtual display

Headless Chrome produces a different fingerprint from the Chrome a person runs on a desktop, and FingerprintJS picks up the difference. A full headful browser running inside a virtual display closes most of that gap, and it works on servers with no monitor attached.

Why headless mode gets flagged

FingerprintJS reads rendering and hardware signals, and headless Chrome answers several of them inconsistently:

  • WebGL behaves differently than it does in a desktop browser.
  • Canvas rendering produces distinct outputs.
  • The audio context reveals the headless state.

Other libraries read the same signals. ThumbmarkJS, a common FingerprintJS alternative, also builds its fingerprint from canvas, WebGL and audio data, so fixing these inconsistencies helps on sites that run a different library too.

Set up Xvfb and pyvirtualdisplay

Xvfb (X virtual framebuffer) is a display server that draws into memory instead of onto a screen. Chrome treats it as a real display and runs in normal headful mode. Install it on Linux:

sudo apt install xvfb

Then control it from Python with the pyvirtualdisplay package and Selenium. The script below starts a 1920 by 1080 virtual screen, launches headful Chrome and reads the visitor ID from the FingerprintJS demo page. Use that ID to confirm what the library sees before you point the setup at a real target:

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 renders pages the way a desktop browser would, so FingerprintJS sees a normal browser environment and the headless-specific rendering signals disappear.

Trade-offs and gaps to check

  • Resource cost. Headful Chrome uses more CPU and RAM than headless mode, so expect to run fewer browsers in parallel on the same server.
  • Screen size. The size you pass to Display becomes the screen resolution the page reports. Keep it at a common desktop resolution like the one in the example.
  • Automation flags. A virtual display fixes rendering only. Selenium still marks the browser as automated, so combine this method with the other techniques in this guide.
  • GPU. Xvfb has no graphics hardware behind it, so WebGL can report a software renderer in place of a real GPU. Check the WebGL vendor and renderer values on the demo page before you rely on the setup.

When the target uses Fingerprint Pro. The paid Fingerprint Pro script loads from vendor domains that ad blockers recognize, and in one round of testing it failed to run in 21 of 24 privacy-tool sessions. A content blocker in your browser profile can stop it before it collects anything. The free open-source FingerprintJS library is self-hosted on the site's own domain, so blockers leave it alone, and that is where the virtual display approach does its work.

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 are paid (Multilogin starts around $100/month) but remove fingerprint-engineering work. Both ship updates when new detection vectors appear.

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:[email protected]: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 that 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 your 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.