# curl_cffi 프록시 가이드: 설정, 인증, 회전 및 비동기

curl_cffi는 브라우저 모양의 TLS 핸드셰이크를 제공합니다. 프록시는 깨끗한 종료 IP를 제공합니다. 두 가지를 함께 연결하는 방법과 프록시 dict의 https:// 접두사가 ErrCode 35를 발생시키는 이유를 정확히 설명합니다.

curl_cffi는 curl-impersonate 포크에 대한 파이썬 바인딩입니다: urllib3로 자신을 알리는 대신 실제 브라우저의 TLS/JA3 및 HTTP/2 지문을 재현합니다. 이는 차단의 한 축을 해결합니다. 다른 하나는 종료 IP로, 이는 curl_cffi 프록시가 필요한 이유이며 문서가 부족한 부분입니다. 공식 프록시 섹션은 약 15줄 정도이며, 2023년 2월의 GitHub 이슈는 여전히 이 주제에서 상위 5위 안에 듭니다. 이 가이드는 전체 표면을 다룹니다: `proxy` 매개변수, 요청 스타일의 dict 및 실제 키 이름, `proxy_auth`, 세션, 요청별 회전, 비동기, SOCKS5 — 그리고 검색 상자에 붙여넣을 정확한 오류 문자열들입니다.

## curl_cffi 프록시 구문: proxies dict보다 proxy=를 선호

curl_cffi는 두 가지 형식을 허용합니다. 기본 형식은 v0.6.0에 추가된 단일 `proxy=` 문자열이며, `proxies=` dict는 requests 호환성을 위해 존재하며, 문서에서는 실제로 스키마별로 다른 프록시가 필요하지 않는 한 단일 매개변수를 권장합니다. 내부적으로는 동일한 것으로 축소됩니다 — `proxy="..."`는 `{"all": "..."}`로 변환됩니다 — 그리고 모듈 도우미, `Session`, `AsyncSession` 및 개별 요청에서 모두 작동합니다.

```python
# pip install curl_cffi --upgrade    (Python 3.10+ since v0.14)
import curl_cffi

PROXY = "http://USER:PASS@residentialpremium.quantumproxies.io:8080"

# Native form — one string, applies to every scheme
r = curl_cffi.get(
    "https://tls.browserleaks.com/json",
    impersonate="chrome",
    proxy=PROXY,
    timeout=30,
)
print(r.status_code, r.json()["ja3n_hash"])

# requests-compatible form
r = curl_cffi.get(
    "https://httpbin.org/ip",
    impersonate="chrome",
    proxies={"http": PROXY, "https": PROXY},
    timeout=30,
)
print(r.json())  # {'origin': '<proxy exit IP>'}
```

README에서 명확하지 않은 네 가지 사항이 있습니다:

- **유효한 키는 `all`, `http`, `https`, `ws` 및 `wss`입니다.** `all`은 모든 것을 포괄합니다; 웹소켓 키는 WebSocket 클라이언트에만 중요합니다.
- **호스트별 키도 작동합니다.** `https://api.example.com` 또는 `all://example.com`은 해당 호스트를 주어진 프록시를 통해 라우팅합니다 — 어려운 도메인을 주거용 IP를 통해 보내고 나머지는 직접 연결하는 데 유용합니다.
- **둘 다 전달할 수 없습니다.** 동일한 호출에서 `proxy=`와 `proxies=`를 함께 전달하면 `TypeError: Cannot specify both 'proxy' and 'proxies'`가 발생하며, 동일한 검사가 세션 수준에서도 실행됩니다.
- **환경 변수가 존중됩니다** — `http_proxy`, `https_proxy`, `ws_proxy`, `wss_proxy`. 회사 변수가 스크래퍼를 가로채는 경우 `trust_env=False`를 `Session`에 전달하십시오.

임포트에 대한 한 가지 주의 사항: v0.10.0 이후 패키지는 직접 호출 가능합니다 (`curl_cffi.get`, `curl_cffi.Session`). 이전 튜토리얼에서는 `from curl_cffi import requests`를 사용하며, 이는 여전히 작동하지만 실제 requests 라이브러리 옆에서 읽기 나쁩니다 — 그리고 온라인의 절반의 코드 조각이 다른 프로젝트처럼 보이는 이유를 설명합니다.

## https:// 함정: ErrCode 35 및 WRONG_VERSION_NUMBER

이 단일 실수는 다른 모든 것보다 더 많은 curl_cffi 프록시 질문을 생성합니다. 프로젝트 트래커의 issue #6 — 2023년 2월 같은 날에 열리고 닫혔습니다 — 여전히 페이지 1에 랭크되어 있으며, 생성된 오류가 TLS 버그처럼 보이기 때문입니다:

```python
# WRONG: this asks curl to open a TLS connection *to the proxy itself*
proxies = {"https": "https://USER:PASS@residentialpremium.quantumproxies.io:8080"}

# Failed to perform, ErrCode: 35, Reason:
# 'error:100000f7:SSL routines:OPENSSL_internal:WRONG_VERSION_NUMBER'

# RIGHT: plain HTTP CONNECT, then the TLS tunnel runs through to the target
proxies = {"https": "http://USER:PASS@residentialpremium.quantumproxies.io:8080"}

# Or skip the dict entirely
proxy = "http://USER:PASS@residentialpremium.quantumproxies.io:8080"
```

키는 *대상*의 프로토콜을 나타내고; 값은 *프록시*에 도달하는 방법을 나타냅니다. 일반적인 HTTPS-over-HTTP 프록시는 평문 `CONNECT`를 받아들인 후 암호화된 트래픽을 그대로 터널링하므로, 프록시 URL은 모든 URL이 HTTPS일 때에도 `http://`로 시작합니다. HTTPS-over-HTTPS 프록시는 존재하지만 드물며 게이트웨이에서 명시적으로 지원해야 합니다. 요청은 동일한 실패를 훨씬 더 도움이 되게 설명합니다 — *당신의 프록시는 HTTP만 사용하고 HTTPS는 사용하지 않는 것 같습니다* — 그래서 동일한 구성이 curl_cffi 전용 버그처럼 보일 수 있습니다. 최근 버전은 경고와 issue #6을 링크하지만, 이는 단지 경고일 뿐입니다: 요청은 여전히 실패합니다.

## 인증: URL 자격 증명 또는 proxy_auth

인증된 게이트웨이는 일반적인 내장 형식 `http://USER:PASS@host:port`를 받아들이며, 일반적인 주의 사항이 있습니다: 비밀번호에 이스케이프되지 않은 `@`, `:` 또는 `/`가 있으면 URL이 잘못된 위치에서 분할되어 죽은 프록시처럼 보이는 인증 실패를 발생시킵니다. curl_cffi는 requests가 제공하지 않는 탈출구를 제공합니다 — `proxy_auth` 튜플을 libcurl에 별도의 사용자 이름 및 비밀번호 옵션으로 전달하여 인코딩이 전혀 필요하지 않습니다.

```python
import curl_cffi
from urllib.parse import quote

# Option A — credentials in the URL, password URL-encoded
pw = quote("p@ss:word", safe="")
r = curl_cffi.get(
    "https://httpbin.org/ip",
    proxy=f"http://USER:{pw}@residentialpremium.quantumproxies.io:8080",
    impersonate="chrome",
    timeout=30,
)

# Option B — keep credentials out of the URL entirely
r = curl_cffi.get(
    "https://httpbin.org/ip",
    proxy="http://residentialpremium.quantumproxies.io:8080",
    proxy_auth=("USER", "PASS"),
    impersonate="chrome",
    timeout=30,
)
print(r.json())
```

세 번째 옵션은 이 클래스의 버그를 완전히 제거합니다: IP 화이트리스트. 모든 [QuantumProxies 주거용 플랜](https://quantumproxies.io/residential-proxies)은 사용자:비밀번호를 보내는 대신 서버의 IP를 인증할 수 있도록 하여, 프록시 URL이 단순한 `http://residentialboson.quantumproxies.io:PORT`가 되며 — 인코딩할 것이 없고, 소스 트리에 비밀이 없습니다. 자격 증명 자체가 거부되는 경우, [407 프록시 인증 필요의 모든 원인](https://quantumproxies.io/blog/fix-407-proxy-authentication-required)에 대한 가이드가 나머지를 다룹니다.

![Chrome을 모방한 TLS 핸드셰이크를 쓰는 curl_cffi 요청이 회전 프록시 게이트웨이를 거치는 과정: 평문 CONNECT가 터널을 열고, 핸드셰이크는 손대지 않은 채 통과하며, 대상 사이트는 깨끗한 주거용 출구 IP에서 도착하는 Chrome의 ClientHello와 HTTP/2 설정을 보게 됩니다.](https://res.cloudinary.com/dmqliab0t/image/upload/f_auto,q_auto,w_1200/v1785776553/blog/generated/qp-b10-006-diag1.png)
*프록시는 핸드셰이크를 종료하지 않고 터널링하므로 브라우저 가장이 홉을 통해 살아남고 대상은 깨끗한 종료 IP를 봅니다.*

## 세션, 쿠키 및 자격 증명 재사용 세부사항

`Session`은 쿠키, 연결 풀링 및 기본값을 한 곳에 보관하며, 이는 다단계 작업에 필요한 것입니다. `impersonate`와 `proxy`를 한 번 설정하면 모든 요청이 이를 상속받습니다:

```python
from curl_cffi import Session

with Session(
    impersonate="chrome",
    proxy="http://USER-sid-a1b2-ttl-30m:PASS@residentialpremium.quantumproxies.io:8080",
    timeout=30,
    retry=3,
) as s:
    s.get("https://httpbin.org/cookies/set/foo/bar")
    r = s.get("https://httpbin.org/cookies")
    print(r.json(), s.cookies.get_dict())
```

두 가지 동작은 주목할 만합니다. 첫째, 프록시가 구성될 때마다 curl_cffi는 libcurl의 proxy-credential-no-reuse 옵션을 켭니다: 프록시 사용자 이름이 변경될 때 새로운 연결이 강제되며, TLS 세션 캐시는 프록시 주소에 따라 키가 설정되므로 이전 종료 IP가 재사용된 세션을 통해 나중 요청에 누출될 수 없습니다. 대부분의 회전 게이트웨이가 하는 것처럼 사용자 이름에 스티키 세션 ID를 인코딩하면 무료로 이러한 격리를 얻을 수 있습니다. 둘째, `retry` (정수 또는 `curl_cffi.requests`의 `RetryStrategy`로 지연, 백오프 및 지터 포함)는 전송 예외에서만 다시 실행됩니다. 이는 **403 또는 429를** urllib3의 `status_forcelist`처럼 재시도하지 않습니다 — 그 루프는 여전히 작성해야 합니다. 호환성 문서에서는 재시도를 지원하지 않는다고 나와 있지만, 이는 오래된 정보입니다: 매개변수는 v0.15.0에 도입되었습니다.

[curl_cffi를 회전 주거용 게이트웨이에 지정](https://quantumproxies.io/rotating-proxies)

## 요청별 회전 및 비동기

curl_cffi는 각 요청에서 프록시 회전을 통한 asyncio를 광고하며, 이는 문자 그대로입니다: 개별 호출의 `proxy=` 인수는 세션에 있는 내용을 무시합니다. 이를 활용하기 위해 프록시 목록이 거의 필요하지 않습니다 — 회전 게이트웨이는 각 연결에서 서버 측에서 새로운 종료를 할당하므로, 하나의 엔드포인트와 동시성만으로도 이미 회전입니다. 제어가 필요한 경우 (작업자별, 계정별, 장바구니별 안정적인 IP 하나) 사용자 이름에 세션 토큰을 넣고 게이트웨이가 해당 종료를 고정하도록 하십시오.

```python
import asyncio
from curl_cffi import AsyncSession

GATE = "residentialpremium.quantumproxies.io:8080"
URLS = ["https://httpbin.org/ip"] * 20

async def fetch(session, url, worker):
    # one sticky exit IP per worker; drop the -sid-...-ttl-30m suffix for full rotation
    proxy = f"http://USER-sid-{worker}-ttl-30m:PASS@{GATE}"
    r = await session.get(url, proxy=proxy, timeout=30)
    return r.status_code, r.json()["origin"]

async def main():
    async with AsyncSession(impersonate="chrome", max_clients=10) as s:
        return await asyncio.gather(
            *(fetch(s, u, i % 5) for i, u in enumerate(URLS))
        )

for status, ip in asyncio.run(main()):
    print(status, ip)
```

`max_clients`는 풀의 동시 curl 핸들을 제한합니다 (기본값 10), 따라서 이는 실제 동시성 다이얼입니다 — 무제한 gather에 세마포어를 붙이는 것이 일반적인 실수입니다. 동일한 크기 조정 논리는 [httpx 및 aiohttp를 사용한 비동기 파이썬 스크래핑](https://quantumproxies.io/blog/python-async-scraping-httpx-aiohttp-proxies)에서 다룬 모든 비동기 클라이언트에 적용됩니다. 요청별 회전 또는 세션 고정 여부는 사이트가 요청 간 상태를 추적하는지 여부에 따라 다릅니다; [스티키 세션 대 회전 프록시](https://quantumproxies.io/blog/sticky-sessions-vs-rotating-proxies)의 트레이드오프가 있습니다.

![ErrCode 35의 원인이 되는 curl_cffi 프록시 실수: https:// 프록시 URL을 쓰면 curl이 프록시 자체에 TLS를 열려다 WRONG_VERSION_NUMBER로 실패하고, http://를 쓰면 평문 CONNECT를 보내 대상까지 TLS를 터널링합니다. 카드는 작동하는 설정과 이스케이프하지 않은 비밀번호 같은 흔한 실수를 대조합니다.](https://res.cloudinary.com/dmqliab0t/image/upload/f_auto,q_auto,w_1200/v1785776553/blog/generated/qp-b10-006-diag2.png)
*대부분의 curl_cffi 프록시 실패는 다섯 가지 중 하나입니다 — 그 중 네 가지는 문자열의 단일 문자입니다.*

## SOCKS5, HTTP/3 및 안전 스위치

SOCKS는 추가 설치가 필요 없습니다 — libcurl이 컴파일되어 있으므로, requests와 달리 기억해야 할 `[socks]` 추가 기능이 없습니다. `socks5h://USER:PASS@residentialpremium.quantumproxies.io:1080`를 사용하십시오: `h`는 DNS 해석을 프록시로 밀어내어, 네트워크에서의 누출을 방지하고 종료 위치에서 지리적으로 제한된 호스트 이름을 해석합니다. curl_cffi는 `socks` 접두사를 감지하고 HTTP 터널링 플래그를 건너뜁니다, SOCKS 프로토콜이 이를 자체적으로 처리하기 때문입니다. 여기의 모든 플랜은 동일한 게이트웨이에서 HTTP 및 [SOCKS5 엔드포인트](https://quantumproxies.io/socks5-proxies)를 노출하므로, 전환은 새로운 주문이 아닌 스키마 교체입니다.

- **프록시를 통한 HTTP/3**는 http/3 지문과 함께 v0.15.0에 도입되었지만, UDP를 지원하는 SOCKS5 서버가 필요하며, 일반 HTTP 게이트웨이는 아닙니다. 목표가 QUIC를 보상할 때까지는 틈새입니다.
- **SSRF 강화.** 동일한 릴리스는 권고를 포함했습니다: 다른 사람들이 제공한 URL을 가져오는 경우, 리디렉션이 내부 네트워크로 걸어갈 수 있습니다. `allow_redirects="safe"`를 설정하거나 리디렉션을 끄십시오.
- **디버깅.** v0.15은 CLI를 출시했습니다: `curl-cffi get tls.browserleaks.com/json --impersonate chrome`은 한 줄로 가장이 착륙하는지 여부를 알려주며, 프록시를 탓하기 전에 확인할 수 있습니다.

## curl_cffi와 프록시가 충분할 때

사람들이 예상하는 것보다 더 자주. 대상이 내부 API에서 JSON을 제공하거나 서버 렌더링된 HTML을 제공하고, 유일한 장애물이 지문 검사인 경우, 일치하는 핸드셰이크와 주거용 종료가 브라우저의 비용과 지연의 일부로 이를 해결합니다. 프로젝트의 FAQ는 한계를 명확히 합니다: 지문은 여러 요소 중 하나이며, IP 품질, 요청 속도 및 JavaScript 검사와 함께 있으며, 더 높은 보호 계층은 더 나은 프록시 풀과 실제 브라우저 자동화가 필요합니다. 가장이 올바르게 구성되었고 여전히 차단되는 경우, 남은 변수는 거의 항상 종료 IP입니다 — 이를 5분 내에 격리하는 것이 [curl_cffi 대 requests](https://quantumproxies.io/blog/curl-cffi-vs-requests)의 주제입니다. 둘 다 실행하고 싶지 않다면, [Scraper API](https://quantumproxies.io/scraper-api)는 지문, 프록시 및 선택적 JS 렌더링을 한 번의 호출로 처리합니다.

## 자주 묻는 질문

### curl_cffi에서 프록시를 어떻게 사용하나요?

`proxy="http://USER:PASS@host:port"`를 요청 메서드, 세션 또는 비동기 세션에 전달하십시오. 요청 스타일의 `proxies={"http": ..., "https": ...}` dict도 작동하지만, 프로젝트는 실제로 스키마별로 다른 프록시가 필요하지 않는 한 단일 매개변수를 권장합니다. 둘 다 전달하면 TypeError가 발생합니다.

### curl_cffi가 ErrCode 35 WRONG_VERSION_NUMBER를 던지는 이유는 무엇인가요?

프록시 URL이 `https://`로 시작하기 때문입니다. 표준 프록시는 평문 CONNECT 요청을 기대한 후 TLS를 터널링하므로, `https://` 접두사는 curl이 프록시 자체와 TLS 핸드셰이크를 시도하게 만듭니다, 이는 평문 HTTP로 응답합니다. 값을 `http://`로 변경하십시오 — `https` 키는 홉이 아닌 대상을 나타냅니다.

### curl_cffi가 SOCKS5 프록시를 지원하나요?

예, 기본적으로 지원합니다 — libcurl이 번들되어 있으므로 설치할 추가 옵션이 없습니다. `socks5h://` 스키마를 사용하여 호스트 이름이 기계가 아닌 프록시에 의해 해석되도록 하십시오. SOCKS4, SOCKS4a 및 평문 `socks5://`도 허용됩니다; 라이브러리는 스키마가 socks로 시작하는 모든 프록시에 대해 HTTP 터널링을 건너뜁니다.

### curl_cffi가 모든 요청에서 프록시를 회전할 수 있나요?

예. 개별 호출의 `proxy=` 인수는 `AsyncSession` 내에서도 세션 기본값을 무시합니다, 이는 README에서 요청별 회전과 함께 asyncio를 의미합니다. 회전 게이트웨이와 함께라면 논리가 거의 필요하지 않습니다: 동일한 엔드포인트가 연결당 다른 종료 IP를 제공합니다.

### curl_cffi가 Cloudflare를 우회할 수 있나요?

때때로 가능합니다. 이는 TLS 및 HTTP/2 지문을 제거하여 기본 보호 수준에 충분합니다. JavaScript 챌린지를 실행하거나 Turnstile을 해결하거나 이미 평판 데이터베이스에 플래그가 지정된 데이터센터 IP를 복구할 수 없습니다. 가장을 세 가지 요구 사항 중 하나로 취급하고, 답이 아닙니다.

전체 구성은 명성보다 작습니다: 하나의 `proxy` 문자열, 세션에 한 번 설정된 `impersonate`, 모든 호출에 대한 타임아웃, URL 인코딩된 자격 증명 또는 `proxy_auth` 튜플로 전달된 자격 증명. 이를 올바르게 설정하고 남은 변수는 IP 품질입니다 — 플래그가 지정된 데이터센터 주소에서 완벽한 Chrome 핸드셰이크는 여전히 플래그가 지정된 데이터센터 주소입니다. [JA3 및 JA4 지문](https://quantumproxies.io/blog/tls-ja3-fingerprinting-scraping)에 대한 우리의 분석은 두 가지 검사가 독립적임을 설명합니다.

[가장에 맞는 주거용 IP를 얻으세요](https://quantumproxies.io/residential-proxies)
