Created
May 8, 2026 07:44
-
-
Save rubenvereecken/9e9196cd9e3dde15e72c9f4d43b45c9a to your computer and use it in GitHub Desktop.
Camoufox config — non-default features, HTTP/3 leak fix, fingerprint overrides
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| """ | |
| Example Camoufox config — features that aren't on by default. | |
| Ruben Vereecken, 2026-05-08 | |
| Usage: | |
| python camoufox-example-config.py | |
| python camoufox-example-config.py --proxy socks5://localhost:1082 | |
| python camoufox-example-config.py --proxy http://user:pass@host:port | |
| python camoufox-example-config.py --proxy http://host:port --with-h3 | |
| """ | |
| import random | |
| import sys | |
| from camoufox.async_api import AsyncCamoufox | |
| from camoufox.fingerprints import generate_context_fingerprint, get_random_preset | |
| TARGET_OS = "macos" | |
| TEST_URL = "https://checker-test.proxyshard.com/" | |
| def build_fingerprint(*, disable_h3: bool = True): | |
| """Build a full fingerprint config with our recommended overrides.""" | |
| preset = get_random_preset(os=TARGET_OS) | |
| # config_overrides are applied BEFORE the init_script is rendered, | |
| # so seed values (like fonts:spacing_seed) are baked into the JS. | |
| ctx_fp = generate_context_fingerprint( | |
| preset=preset, | |
| os=TARGET_OS, | |
| config_overrides={ | |
| # Disable font spacing perturbation — the noise pattern itself | |
| # is detectable (population-marginal on fingerprint tests). | |
| # Seed 0 triggers an early return in the C++ GetSeed(). | |
| "fonts:spacing_seed": 0, | |
| # Spoof codec enumeration (canPlayType) to match target OS. | |
| "media:spoof_codecs": True, | |
| }, | |
| ) | |
| # Post-fingerprint config — merged after init_script is built. | |
| extra_config = { | |
| # A fresh browser always shows history.length=1, which is a signal. | |
| "window.history.length": random.randint(1, 5), | |
| } | |
| config = {**ctx_fp["config"], **extra_config} | |
| firefox_prefs = {} | |
| if disable_h3: | |
| # QUIC uses UDP. Most HTTP CONNECT / SOCKS5 proxies only tunnel TCP. | |
| # Without this, the browser tries HTTP/3 over UDP which bypasses the | |
| # proxy and leaks your real IP. This is the exact leak that | |
| # checker-test.proxyshard.com catches. | |
| firefox_prefs["network.http.http3.enable"] = False | |
| return preset, ctx_fp, config, firefox_prefs | |
| async def run(*, proxy: dict | None = None, disable_h3: bool = True): | |
| preset, ctx_fp, config, firefox_prefs = build_fingerprint(disable_h3=disable_h3) | |
| launch_kwargs = dict( | |
| fingerprint_preset=preset, | |
| os=TARGET_OS, | |
| headless=False, | |
| config=config, | |
| firefox_user_prefs=firefox_prefs, | |
| i_know_what_im_doing=True, | |
| ) | |
| if proxy: | |
| launch_kwargs["proxy"] = proxy | |
| async with AsyncCamoufox(**launch_kwargs) as browser: | |
| context = await browser.new_context(**ctx_fp["context_options"]) | |
| await context.add_init_script(ctx_fp["init_script"]) | |
| page = await context.new_page() | |
| print(f"Navigating to {TEST_URL} ...") | |
| print(f" HTTP/3 disabled: {disable_h3}") | |
| print(f" Proxy: {proxy['server'] if proxy else 'none'}") | |
| await page.goto(TEST_URL) | |
| # Dismiss cookie banner if present | |
| try: | |
| accept_btn = page.locator("button:has-text('Accept all')") | |
| if await accept_btn.count() > 0: | |
| await accept_btn.first.click() | |
| await page.wait_for_timeout(1_000) | |
| except Exception: | |
| pass | |
| # Give the checker time to run its network tests | |
| await page.wait_for_timeout(20_000) | |
| # Extract the full visible text from the results area | |
| results = await page.evaluate("""() => { | |
| // Get all text content, structured by section | |
| const sections = document.querySelectorAll('section, [class*="card"], [class*="Card"], [class*="panel"], [class*="result"]'); | |
| if (sections.length > 0) { | |
| return Array.from(sections).map(s => s.innerText.trim().substring(0, 500)); | |
| } | |
| return document.body.innerText.substring(0, 3000); | |
| }""") | |
| screenshot_name = f"proxyshard-{'h3-on' if not disable_h3 else 'h3-off'}.png" | |
| screenshot_path = f"/tmp/{screenshot_name}" | |
| await page.screenshot(path=screenshot_path, full_page=True) | |
| print(f"\nScreenshot saved: {screenshot_path}") | |
| print(f"\nPage results:\n{results}") | |
| if __name__ == "__main__": | |
| import asyncio | |
| # Pass --proxy to route traffic through a proxy. Examples: | |
| # --proxy socks5://localhost:1082 | |
| # --proxy http://user:pass@host:port | |
| proxy = None | |
| if "--proxy" in sys.argv: | |
| idx = sys.argv.index("--proxy") | |
| if idx + 1 < len(sys.argv): | |
| proxy = {"server": sys.argv[idx + 1]} | |
| # Pass --with-h3 to leave HTTP/3 enabled (to reproduce the leak) | |
| disable_h3 = "--with-h3" not in sys.argv | |
| asyncio.run(run(proxy=proxy, disable_h3=disable_h3)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment