Python'da requests ile bir anti-bot korumalı siteye istek attığınızda 403 alıyorsanız, sorun büyük olasılıkla içeriğinizde değil — TLS el sıkışmanızda. curl_cffi ile TLS taklidi, ClientHello paketinizi gerçek bir Chrome tarayıcısından ayırt edilemez hale getirerek bu engeli aşmanın en pratik yoludur. Bu rehberde, JA3/JA4 parmak izlerinin nasıl çalıştığını, curl_cffi'nin BoringSSL tabanlı impersonate motorunu nasıl kullandığını ve ProxyHat residential proxy'lerle birleştirildiğinde neden güçlü bir kombinasyon oluşturduğunu inceleyeceğiz.
curl_cffi ile TLS Taklidi Nedir ve Neden Gereklidir?
Modern anti-bot sistemleri (Cloudflare, Akamai, DataDome, Imperva, PerimeterX) artık sadece IP itibarını veya User-Agent başlığını kontrol etmiyor. Bağlantının ilk paketinde — TLS ClientHello — tarayıcıya özgü bir parmak izi arıyorlar. Bu parmak izi, şifreleme paketlerinin sıralaması, TLS uzantılarının düzeni, desteklenen eliptik eğriler ve imza algoritmaları gibi onlarca parametrenin birleşimidir.
Python'un requests kütüphanesi, arka planda urllib3 ve OpenSSL kullanır. OpenSSL'in ClientHello'su, Chrome veya Firefox'tan tamamen farklıdır: şifre sıralaması farklıdır, GREASE değerleri eksiktir, TLS uzantılarının sırası ve sayısı farklıdır. Sonuç olarak, henüz tek bir HTTP baytı göndermeden, sunucu sizin bir bot olduğunuzu bilir.
curl_cffi ile TLS taklidi, lexiforest/curl_cffi projesinin çekirdek yeteneğidir. Bu kütüphane, curl-impersonate projesinin BoringSSL yamasını Python'a taşır ve impersonate="chrome" parametresiyle ClientHello paketinizi Chrome'unkine birebir benzer şekilde üretir.
Python requests Neden 'Bot' Olarak Etiketlenir?
JA3 Parmak İzi Nasıl Çalışır?
JA3, Salesforce tarafından geliştirilen bir TLS parmak izleme yöntemidir. ClientHello'daki şu alanları birleştirir:
- SSL/TLS sürümü
- Şifre paketleri (cipher suites) — sıralı
- TLS uzantıları — sıralı
- Desteklenen eliptik eğriler (elliptic curves)
- İmza algoritmaları (ec_point_formats)
Bu değerler tire ile birleştirilir ve MD5 hash olarak özetlenir. Örneğin, Python requests + OpenSSL'in tipik JA3 hash'i e7d705a3286e19ea42f587b344ee6865 civarındadır. Cloudflare bu hash'i bilinen bot listesinde tutar ve bağlantıyı daha ilk pakette işaretler.
Chrome 120 ise farklı bir JA3 üretir — GREASE değerleri içerir, şifre sıralaması farklıdır, signature_algorithms uzantısında 16+ algoritma listeler. Aşağıdaki tablo temel farkları özetler:
| Parametre | Python requests (OpenSSL) | Chrome 120 (BoringSSL) |
|---|---|---|
| Şifre paketi sayısı | ~15, GREASE yok | ~17, GREASE değerleri içerir |
| TLS uzantı sayısı | ~12 | ~18, application_layer_protocol_negotiation dahil |
| supported_versions sırası | TLS 1.3 → TLS 1.2 | GREASE karıştırılmış, TLS 1.3 önce |
| key_share grupları | x25519, secp256r1 | GREASE, x25519, secp256r1, secp384r1 |
| HTTP/2 SETTINGS frame | Yok (HTTP/1.1) | Chrome'a özel SETTINGS sıralaması |
JA4 ve Sıralama Kararlılığı
JA3'ün sorunu, şifre paketlerinin sırasına duyarlı olmasıydı. Chrome 110+ sürümlerinde Google, ClientHello'daki şifre paketlerini her bağlantıda rastgele sıralamaya başladı. Bu, JA3 hash'ini her seferinde değiştirerek statik blok listelerini işe yaramaz hale getirdi.
JA4, FoxIO tarafından bu sorunu çözmek için geliştirildi. JA4, şifre paketlerini ve uzantıları alfabetik olarak sıralar, GREASE değerlerini çıkarır ve böylece sıralama bağımsız bir parmak izi üretir. Amaç, tek bir tarayıcı sürümü için kararlı bir hash elde etmektir — yani Chrome 120'nin tüm ClientHello varyasyonları aynı JA4 hash'ini verir.
Bu, anti-bot sistemler için daha sağlam bir tespit sağlar ama bizim açımızdan da bir uyarıdır: JA4, sıralama numaralarını değiştirerek atlatılamaz. ja3 parmak izi sahteciliği artık şifre sıralamasını karıştırmaktan ibaret değildir — gerçek tarayıcı parmak izini bütünüyle taklit etmeniz gerekir.
curl_cffi Nasıl Çalışır: BoringSSL ve curl-impersonate
curl_cffi, curl-impersonate projesine dayanır. curl-impersonate, libcurl'u BoringSSL ile yeniden derler ve Chrome/Firefox'un TLS yığınını taklit etmek için gerekli yamaları uygular. Bu yamalar şunları içerir:
- Şifre paketi sıralamasının tarayıcıya göre ayarlanması
- TLS uzantılarının tarayıcı sırasına göre dizilmesi
- GREASE değerlerinin doğru konumlara yerleştirilmesi
- HTTP/2 SETTINGS frame'inin tarayıcıya özgü değerlerle gönderilmesi
- supported_versions ve key_share uzantılarının tarayıcı formatında üretilmesi
Python tarafında curl_cffi, bu yetenekleri requests-benzeri bir API ile sunar:
from curl_cffi import requests
# Basit TLS taklidi
r = requests.get(
"https://www.example.com",
impersonate="chrome"
)
print(r.status_code)
impersonate parametresi şu değerleri alabilir: "chrome", "safari", "firefox", "edge" ve sürüme özgü varyantlar ("chrome120", "chrome116", vb.). Her preset, o tarayıcının o sürümü için tam ClientHello konfigürasyonunu yükler.
Gelişmiş Parmak İzi Geçersiz Kılmaları
curl_cffi, daha ince ayar için üç geçersiz kılma mekanizması sunar:
from curl_cffi import requests
r = requests.get(
"https://protected-site.com",
impersonate="chrome120",
ja3="771,4865-4866-4867-49195-49199-...,0-23-65281-10-11-35-16-5-13-18-51-45-43-10-11,29-23-24,0",
akamai="2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
extra_fp={
"tls_signature_algorithms": [
"ecdsa_secp256r1_sha256",
"rsa_pss_rsae_sha256",
"rsa_pkcs1_sha256",
],
"tls_client_hello_random": "01:02:03:04:05:06:07:08:09:0a:0b:0c:0d:0e:0f:10",
}
)
Burada:
ja3: Ham JA3 dizesi — şifre, uzantı, eğri sıralamasını manuel belirtirakamai: HTTP/2 SETTINGS frame'inin hex değerlerini tanımlarextra_fp: İmza algoritmaları, ClientHello random değeri gibi ek parametreleri geçersiz kılar
Bu seviye, özel anti-bot kurallarıyla karşılaştığınızda veya belirli bir tarayıcı sürümünü hedeflediğinizde gereklidir. Çoğu durumda impersonate="chrome" yeterlidir.
Neden Residential Proxy'ler Hala Zorunlu?
Mükemmel bir TLS parmak izi, veri merkezi IP'si üzerinden hala başarısız olur. Neden? Çünkü anti-bot sistemler TLS parmak izini sadece bir sinyal olarak kullanır — IP itibar puanı, ASN sınıflandırması, geçmiş davranış verisi ve JS challenge sonuçlarıyla birleştirir.
Bir AWS us-east-1 IP'si (AS14618) üzerinden Chrome mükemmel TLS parmak iziyle bağlansanız bile, Cloudflare şunu görür:
- TLS parmak izi: Chrome ✓
- IP ASN: AWS datacenter ✗
- Geçmiş istek hacmi: bu IP'den son 24 saatte 50.000 istek ✗
- JS challenge: çalıştırılmadı ✗
4 sinyalden 1'i geçer, 3'ü başarısız. Sonuç: 403 veya sessizce captcha.
Residential proxy'ler bu denklemi değiştirir. Bir Alman ISP'sinden (Deutsche Telekom, AS3320) gelen bir IP, sıradan bir kullanıcı gibi görünür. TLS parmak izi Chrome ise, IP itibarı temizse ve istek hızı makulsa, bağlantı büyük olasılıkla geçer.
ProxyHat, 195+ lokasyonda residential proxy çıkışları sunar. Her IP, gerçek bir ISP'ye atanmıştır ve datacenter ASN'lerinden ayrılır. ProxyHat fiyatlandırması, gigabyte başına şeffaf model kullanır — gizli ücret yoktur.
Çalışan Örnek: curl_cffi + ProxyHat Residential Proxy
Aşağıdaki örnek, curl_cffi'nin AsyncSession API'sini ProxyHat residential çıkışlarıyla birleştirir. Almanya lokasyonu kullanır, otomatik rotasyon ve retry içerir.
Temel Kurulum
import asyncio
from curl_cffi.requests import AsyncSession
# ProxyHat residential proxy — Almanya lokasyonu
# Kullanıcı adında country-DE bayrağı ile geo-targeting
PROXY_URL = "http://user-country-DE:YOUR_PASSWORD@gate.proxyhat.com:8080"
async def fetch_with_tls_impersonation():
async with AsyncSession(impersonate="chrome120") as session:
try:
r = await session.get(
"https://httpbin.org/headers",
proxies={"https": PROXY_URL, "http": PROXY_URL},
timeout=30,
)
print(f"Status: {r.status_code}")
print(f"Response: {r.json()}")
return r
except Exception as e:
print(f"Error: {e}")
raise
asyncio.run(fetch_with_tls_impersonation())
Sticky Session ile Çoklu İstek
Bazen aynı IP'de kalmanız gerekir — örneğin, oturum açma ardından sayfa gezinme. ProxyHat, kullanıcı adında session-XXX bayrağıyla sticky session destekler:
import asyncio
from curl_cffi.requests import AsyncSession
# Sticky session: aynı IP'de kal
# session-mytask123 kimliği 10 dakika boyunca aynı IP'yi döndürür
PROXY_URL = (
"http://user-session-mytask123-country-DE:"
"YOUR_PASSWORD@gate.proxyhat.com:8080"
)
async def scrape_multiple_pages():
async with AsyncSession(impersonate="chrome120") as session:
urls = [
"https://httpbin.org/headers",
"https://httpbin.org/ip",
"https://httpbin.org/user-agent",
]
results = []
for url in urls:
r = await session.get(
url,
proxies={"https": PROXY_URL, "http": PROXY_URL},
timeout=30,
)
results.append({"url": url, "status": r.status_code})
# İstekler arası kısa bekleme
await asyncio.sleep(1.5)
return results
asyncio.run(scrape_multiple_pages())
Retry ve Rotasyon Mantığı
Üretim ortamında, retry ve otomatik IP rotasyonu gerekir. ProxyHat her istekte yeni IP atayabilir — sticky session bayrağı kullanmadığınızda, her bağlantı farklı bir residential IP'den gelir:
import asyncio
import random
from curl_cffi.requests import AsyncSession
# Rotasyon: sticky session yok → her istekte yeni IP
PROXY_URL = "http://user-country-DE:YOUR_PASSWORD@gate.proxyhat.com:8080"
MAX_RETRIES = 3
BASE_DELAY = 2.0 # saniye
async def fetch_with_retry(url, session):
for attempt in range(MAX_RETRIES):
try:
r = await session.get(
url,
proxies={"https": PROXY_URL, "http": PROXY_URL},
timeout=30,
impersonate="chrome120",
)
if r.status_code == 200:
return r
elif r.status_code == 429:
delay = BASE_DELAY * (2 ** attempt) + random.uniform(0, 1)
print(f"Rate limited, waiting {delay:.1f}s")
await asyncio.sleep(delay)
else:
print(f"HTTP {r.status_code}, retry {attempt + 1}")
await asyncio.sleep(BASE_DELAY)
except Exception as e:
print(f"Attempt {attempt + 1} failed: {e}")
await asyncio.sleep(BASE_DELAY * (2 ** attempt))
raise RuntimeError(f"Failed after {MAX_RETRIES} retries")
async def main():
async with AsyncSession() as session:
urls = [
"https://httpbin.org/headers",
"https://httpbin.org/ip",
"https://httpbin.org/uuid",
]
for url in urls:
r = await fetch_with_retry(url, session)
print(f"{url} → {r.status_code}")
asyncio.run(main())
curl Komut Satırı Alternatifi
Hızlı test için curl_cffi'ye gerek yok — ProxyHat'ı doğrudan curl ile kullanabilirsiniz (ama TLS taklidi için curl-impersonate binary'si gerekir):
# curl-impersonate-chrome ile ProxyHat residential
# HTTP proxy, Almanya lokasyonu
curl_chrome116 \
--proxy http://user-country-DE:YOUR_PASSWORD@gate.proxyhat.com:8080 \
https://httpbin.org/headers
# SOCKS5 alternatifi
curl_chrome116 \
--socks5-hostname user-country-DE:YOUR_PASSWORD@gate.proxyhat.com:1080 \
https://httpbin.org/headers
curl_cffi'nin Sınırları ve Etik Kullanım
JS Challenge'ları Çözemez
curl_cffi, TLS katmanında mükemmel taklidi yapar ama JavaScript çalıştıramaz. Cloudflare Turnstile, DataDome JS challenge, Akamai sensor — bunların tümü bir JavaScript motoru gerektirir. Eğer hedef siteniz JS challenge kullanıyorsa, curl_cffi tek başına yetersizdir.
Bu durumda iki seçeneğiniz var:
- Playwright/Puppeteer + curl_cffi proxy ayarları: Gerçek tarayıcı çalıştırın, ProxyHat'ı tarayıcı proxy'si olarak kullanın. TLS taklidi doğal olarak gelir çünkü gerçek tarayıcıdır.
- Hibrit yaklaşım: curl_cffi ile ilk istekleri yapın, JS challenge gerektiğinde headless browser'a geçin.
Web scraping use case rehberimizde bu hibrit yaklaşımı detaylı anlatıyoruz.
Yasal ve Etik Çerçeve
TLS taklidi, araçların nasıl çalıştığını anlamak ve kendi altyapınızı test etmek için meşru bir tekniktir. Ancak, eriştiğiniz verinin niteliği önemlidir:
- robots.txt ve ToS: Hedef sitenin kullanım şartlarını kontrol edin. Bazı siteler otomatik erişimi açıkça yasaklar.
- GDPR ve KVKK: Kişisel veri içeren sayfaları işliyorsanız, Avrupa Birliği ve Türkiye veri koruma mevzuatına uyum gerekir. Anonimleştirme veya yasal dayanak (meşru menfaat, rıza) şarttır.
- CFAA (ABD): Amerika Birleşik Devletleri'nde, yetkisiz erişim Computer Fraud and Abuse Act kapsamında suç teşkil edebilir. Yalnızca herkese açık verilere ve kendi test ortamınıza erişin.
- Hız sınırları: İstek hızınız hedef sunucuya zarar vermeyecek seviyede olsun. Saniyede 10 istek, çoğu site için makul bir üst sınırdır.
Güvenlik araştırmacıları için ProxyHat dokümantasyonu, yetkili penetrasyon testi ve hata ödül programları için proxy kullanım kılavuzu içerir. SERP tracking use case sayfamızda arama motoru sonuçlarının etik toplama yöntemlerini bulabilirsiniz.
curl_cffi vs Diğer Yaklaşımlar
| Yaklaşım | TLS Taklidi | JS Desteği | Hız (req/saniye) | Bellek Kullanımı |
|---|---|---|---|---|
| Python requests | Yok | Yok | ~500 | Düşük (~20 MB) |
| curl_cffi (impersonate) | Evet (Chrome/Safari/Firefox) | Yok | ~300 | Düşük-Orta (~40 MB) |
| Playwright headless | Doğal (gerçek tarayıcı) | Evet | ~10-20 | Yüksek (~150-300 MB/sayfa) |
| Playwright + curl_cffi proxy | Doğal | Evet | ~10-20 | Yüksek |
Tablodan görüleceği gibi, curl_cffi hız ve TLS taklidi arasında en iyi dengeyi sunar. JS challenge gerekmeyen sitelerde saniyede 300 isteğe kadar ölçeklenir — bu, Playwright'dan 15-30 kat daha hızlıdır.
Sık Yapılan Hatalar ve Edge Case'ler
1. Eski impersonate Sürümü Kullanmak
Anti-bot sistemler sürekli güncellenir. impersonate="chrome" genel preset'i bazen eski Chrome sürümlerini hedefler. 2026'da Chrome 130+ yaygınken, impersonate="chrome120" kullanmak yeni bir parmak izi verir ama gerçek kullanıcıların çoğu daha yeni sürümlerdedir. Mümkünse en güncel preset'i kullanın (chrome131, chrome124, vb.).
2. User-Agent ile TLS Parmak İzi Uyuşmazlığı
En sık yapılan hatalardan biri: impersonate="chrome120" kullanıp User-Agent'da Chrome/118 yazmak. Anti-bot sistemler bu uyuşmazlığı anında tespit eder. curl_cffi, impersonate preset'ine uygun User-Agent otomatik gönderir — manuel olarak geçersiz kılmayın:
# YANLIŞ — UA ile TLS parmak izi çelişiyor
r = requests.get(
url,
impersonate="chrome120",
headers={"User-Agent": "Mozilla/5.0 ... Chrome/118.0.5993.70"}
)
# DOĞRU — curl_cffi UA'yı otomatik ayarlar
r = requests.get(url, impersonate="chrome120")
3. HTTP/2 Ayarlarını Atlamak
curl_cffi, HTTP/2 SETTINGS frame'ini de taklit eder. Ama bazı proxy'ler HTTP/2'yi desteklemez veya bozar. ProxyHat HTTP proxy'leri HTTP/2'yi transparent olarak destekler — ek yapılandırma gerekmez. Eğer başka bir proxy kullanıyorsanız, HTTP/2 desteğini doğrulayın.
4. Canvas ve WebGL Parmak İzi
TLS taklidi, ağ katmanını kapsar. Ama tarayıcıda çalışmıyorsanız, canvas/WebGL parmak izi sorunu olmaz — çünkü JS çalışmıyorsa bu API'lar çağrılmaz. Eğer JS challenge'ı geçerseniz ve site canvas parmak izi alırsa, curl_cffi bu konuda yardımcı olamaz. Bu noktada gerçek tarayıcı gerekir.
Key Takeaways
Önemli Çıkarımlar:
- Python requests'in JA3/JA4 parmak izi, anti-bot sistemler tarafından anında tanınır — TLS taklidi olmadan başarısız olursunuz.
- curl_cffi, BoringSSL tabanlı
impersonatemotoruyla Chrome/Safari/Firefox ClientHello'yu birebir taklit eder.- JA4, sıralama bağımsızdır — şifre sıralamasını karıştırmak artık yeterli değildir; gerçek tarayıcı parmak izini bütünüyle taklit etmeniz gerekir.
- Mükemmel TLS parmak izi bile datacenter IP üzerinde başarısız olur — residential proxy'ler zorunludur.
- ProxyHat residential proxy'lerle curl_cffi kombinasyonu, TLS katmanı + IP itibarı katmanını birlikte çözer.
- curl_cffi JS challenge'ları çözemez — bu durumda Playwright gibi gerçek tarayıcı çözümlerine geçin.
- Etik kullanım şarttır: herkese açık verilere erişin, robots.txt ve ToS'a saygı gösterin, GDPR/KVKK/CFAA uyumunu gözetin.
Hazır başlamak için ProxyHat fiyatlandırma sayfasını inceleyin ve residential proxy paketinizi seçin. curl_cffi entegrasyonu dakikalar içinde tamamlanır — yukarıdaki kod örneklerini kendi projenize yapıştırın ve YOUR_PASSWORD alanını ProxyHat dashboard şifrenizle değiştirin.






