---
type: note
kind: Explainer
tags: ['chrome-devtools', 'cdp', 'websocket', 'proxy']
status: release
ctime: 2026-06-23
mtime: 2026-06-23
generated: { by: claude/opus-4.8, at: 2026-06-28T14:49:32Z }
---

핵심 한 줄: 원격 호스팅 DevTools 프론트엔드(`appspot.com`)를 로컬 CDP에 붙이되, 중간에 **로컬 프록시(9223)를 끼워 Chrome의 cross-origin WebSocket 거부를 우회**한다.

<img
  src="/svg/aiohttp_proxy_origin_laundering.svg"
  alt="프론트엔드 WS를 로컬 프록시(9223)가 종단하고, 파이썬이 Origin 없는 새 WS로 9222에 재접속해 Chrome의 cross-origin 거부를 우회하는 구조"
/>

연결 경로:

- `chrome_devtools_remote` (unix socket) → `adb forward`로 `localhost:9222`에 노출
- `localhost:9222`에서 `/json`으로 탭 목록, `/devtools/page/{id}`로 페이지별 CDP WS 제공
- `localhost:9223` aiohttp 프록시가 프론트엔드 WS를 받아 9222로 양방향 중계
- DevTools 프론트엔드는 `termux-open-url`로 브라우저에서 실행, `?ws=localhost:9223/{id}`로 백엔드 지정

<img
  src="/svg/devtools_py_full_lifecycle.svg"
  alt="adb forward로 소켓을 노출하고 프록시를 띄운 뒤 DevTools 프론트엔드를 실행하기까지의 전체 연결 수명주기"
/>

트릭의 본질 — 왜 프록시가 필요한가:

- 프론트엔드 origin은 `https://chrome-devtools-frontend.appspot.com` → 9222 직결 시 Chrome CDP가 외부 Origin WS를 거부 (DNS rebinding 방어)
- 프록시가 프론트엔드 WS를 종단하고 **파이썬 프로세스가 새 WS로** 9222에 재접속 → 브라우저 Origin이 없으니 통과 ("Origin laundering"). `cors_middleware`가 허용 헤더를 덧붙인다.

기억해둘 것:

- `ws://localhost`이 HTTPS 페이지에서 mixed-content로 안 막히는 이유: **localhost는 secure context로 취급** → IP/도메인이었으면 `wss` 강제로 깨졌을 것.
- 더 깔끔한 대안: 프록시를 안 쓰고 프론트엔드를 `localhost`에서 self-host하면 Origin 문제 자체가 사라진다.
- `?@f84901f7…` = DevTools 프론트엔드 빌드 리비전 핀.
