vgrok - 터널링의 핵심은 역방향 영속 연결
localhost는 inbound 연결을 못 받는다(NAT/방화벽 뒤). 터널링의 원리는 공개 머신에서 로컬로 향하는 영속 연결을 미리 깔아두고, 외부 요청을 그 연결 위로 거꾸로 흘려보내는 것.
vgrok에서 그 영속 연결은 WebSocket이다. local → sandbox 방향으로 한 번 열어두면, sandbox로 들어온 HTTP 요청을 같은 소켓에 실어 local로 되돌릴 수 있다.
외부 요청 ──HTTP──▶ [공개 머신] server.js ──┐
│ WebSocket (local이 미리 연결해둠)
localhost ◀──HTTP── [local] client.js ◀──┘
요청/응답 매칭
WebSocket은 하나인데 동시 요청은 여럿. 그래서 요청마다 UUID를 붙이고, 공개 머신 쪽에서 Map<id, res>로 응답 객체를 들고 있다가 돌아온 메시지의 id로 짝을 찾는다.
// [server] 외부 요청 수신 → 보류 + 전달
const id = randomUUID()
responses.set(id, res)
client.send(JSON.stringify({ id, method, url, headers, body }))
// [server] WS로 응답 돌아오면 id로 원래 res 복원
const { id, statusCode, headers, body } = JSON.parse(msg)
responses
.get(id)
.writeHead(statusCode, headers)
.end(Buffer.from(body, 'base64url'))
body는 base64url — 이미지·파일 같은 바이너리를 JSON에 안전하게 싣기 위함.
Sandbox는 “터널 머신”의 일회용 대체재
여기서 신선한 점: 보통 터널링은 ngrok처럼 전용 공개 서버가 필요한데, vgrok은 Vercel Sandbox를 그 자리에 끼워넣는다. Sandbox가 마침 두 가지를 다 주기 때문:
sandbox.domain(port)→ 즉시 공개 HTTPS URL (도메인/인증서 설정 0)- 임의 코드 실행 → WebSocket 프록시(server.js)를 그 위에 배포
const sandbox = await Sandbox.create({ runtime: 'node22', ports: [3000] })
const url = sandbox.domain(3000) // 외부 진입점
await sandbox.writeFiles([{ path: 'server.js', content }])
await sandbox.runCommand({ cmd: 'node', args: ['server.js'], detached: true })
즉 “공개 URL + 코드 실행”이 되는 임시 컴퓨팅이라면 무엇이든 터널 머신이 될 수 있고, Sandbox는 그걸 가장 싸게 얻는 한 수단일 뿐.
한계
- Hobby 45분 타임아웃, HTTP만(TCP 터널 X), 단일 클라이언트
- 핵심 코드 ~220줄 — 네트워킹/보안/HTTPS/DNS를 Sandbox가 다 추상화해서 가능
핵심 한 줄: 원격 호스팅 DevTools 프론트엔드(appspot.com)를 로컬 CDP에 붙이되, 중간에 로컬 프록시(9223)를 끼워 Chrome의 cross-origin WebSocket 거부를 우회한다.
연결 경로:
chrome_devtools_remote(unix socket) →adb forward로localhost:9222에 노출localhost:9222에서/json으로 탭 목록,/devtools/page/{id}로 페이지별 CDP WS 제공localhost:9223aiohttp 프록시가 프론트엔드 WS를 받아 9222로 양방향 중계- DevTools 프론트엔드는
termux-open-url로 브라우저에서 실행,?ws=localhost:9223/{id}로 백엔드 지정
트릭의 본질 — 왜 프록시가 필요한가:
- 프론트엔드 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 프론트엔드 빌드 리비전 핀.