Use Playwright with Chromium flags --enable-quic and --origin-to-force-quic-on to deterministically reproduce HTTP/3 QUIC stalls that only appear in real browsers with cached alt-svc headers. Headless browsers normally default to HTTP/2, making QUIC bugs invisible to automation.
You suspect HTTP/3 (QUIC) is causing page hangs on a Cloudflare-fronted site, but headless browsers default to HTTP/2 (no cached alt-svc header), so you cannot reproduce the stall programmatically. Manual browser testing with chrome://flags is slow and not automatable.
Chromium has an --origin-to-force-quic-on flag that forces QUIC from the first request, bypassing alt-svc discovery. Combine it with Playwright (or Puppeteer) to build a deterministic repro harness.
Key flags:
Playwright example:
import { chromium } from 'playwright';
const browser = await chromium.launch({
headless: true,
args: [
'--enable-quic',
'--origin-to-force-quic-on=example.com:443',
'--quic-version=h3',
],
});
const page = await (await browser.newContext()).newPage();
for (let i = 0; i < 20; i++) {
const start = Date.now();
try {
await page.goto('https://example.com/page', {
waitUntil: 'domcontentloaded',
timeout: 15000,
});
console.log(`Run ${i + 1}: ${Date.now() - start}ms - ok`);
} catch (err) {
console.log(`Run ${i + 1}: ${Date.now() - start}ms - TIMEOUT`);
}
}
await browser.close();Run the same script without the QUIC flags as a control. In our case this produced:
This is useful evidence for hosting provider support tickets (e.g. asking Render to disable HTTP/3 on their managed Cloudflare zone).
Normally, browsers discover HTTP/3 support via the alt-svc response header, then cache it and upgrade on subsequent requests. Headless browsers start fresh each launch with no cached alt-svc, so they never upgrade to QUIC. The --origin-to-force-quic-on flag skips the discovery phase and uses QUIC immediately, matching what happens in a real browser that has already cached the alt-svc header.