From 3c4bc633fe1a82b433d0b7578faa1e14a4bdf984 Mon Sep 17 00:00:00 2001 From: jtricerolph Date: Thu, 9 Jul 2026 17:36:27 +0000 Subject: [PATCH] Revert to headed Chrome + Xvfb; add shm_size 256m to fix Xvfb in Docker Headless mode gets blocked by Cloudflare on the search endpoint. Headed mode with Xvfb works on dev (Unraid). The missing piece on Proxmox LXC was insufficient /dev/shm (Docker default 64 MB); setting shm_size: 256m gives Xvfb enough shared memory to start. Co-Authored-By: Claude Sonnet 4.6 --- backend/Dockerfile | 5 ++++- .../services/scraper_backends/playwright_local.py | 12 ++++-------- docker-compose.yml | 1 + 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/backend/Dockerfile b/backend/Dockerfile index 0d810ed..e59aa13 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -7,6 +7,7 @@ RUN apt-get update && apt-get install -y \ libpq-dev \ postgresql-client \ curl \ + xvfb \ && rm -rf /var/lib/apt/lists/* COPY requirements.txt . @@ -19,4 +20,6 @@ COPY . . EXPOSE 8000 -CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"] +ENV DISPLAY=:99 + +CMD ["sh", "-c", "Xvfb :99 -screen 0 1280x720x24 -nolisten tcp & sleep 1 && uvicorn main:app --host 0.0.0.0 --port 8000"] diff --git a/backend/services/scraper_backends/playwright_local.py b/backend/services/scraper_backends/playwright_local.py index cb54fc7..4ed99ea 100644 --- a/backend/services/scraper_backends/playwright_local.py +++ b/backend/services/scraper_backends/playwright_local.py @@ -56,8 +56,8 @@ class PlaywrightLocalBackend(ScraperBackend): Local Playwright backend using Chromium. Stealth measures: - - Runs headful (via xvfb in the container) — headless leaks SwiftShader - WebGL, empty plugins, missing chrome.runtime + - Runs headful (via Xvfb in the container) — headless leaks SwiftShader + WebGL, empty plugins, missing chrome.runtime; Xvfb needs shm_size 256m - playwright-stealth patches navigator.webdriver, plugins, WebGL vendor - ONE consistent modern-Chrome identity: UA + matching sec-ch-ua client hints + platform (a mismatched UA is worse than none) @@ -159,17 +159,13 @@ class PlaywrightLocalBackend(ScraperBackend): if self._playwright is None: self._playwright = await async_playwright().start() launch_kwargs = dict( - # Headless — stealth patches + residential proxy + session - # cookies cover the fingerprint; headed+xvfb is unreliable - # in Proxmox LXC kernels where Xvfb exits immediately. - headless=True, + headless=False, args=[ '--disable-blink-features=AutomationControlled', '--no-sandbox', '--disable-dev-shm-usage', '--disable-features=IsolateOrigins,site-per-process', - '--disable-gpu', - '--enable-unsafe-swiftshader', + '--start-maximized', ], ) # Proxy is set at launch (Chromium binds the sticky session, which diff --git a/docker-compose.yml b/docker-compose.yml index 19d0130..c981fe0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,6 +3,7 @@ services: build: ./backend security_opt: - apparmor=unconfined + shm_size: '256m' environment: - DATABASE_URL=${DATABASE_URL} - CENTRAL_AUTH_SECRET=${CENTRAL_AUTH_SECRET}