curl_cffi ile TLS Taklidi Nedir ve Neden Önemli?
Modern anti-bot sistemleri artık yalnızca IP adresi veya User-Agent başlığına bakmıyor. 2026 itibarıyla Cloudflare, Akamai, Datadome ve PerimeterX gibi sağlayıcılar, bağlantının TLS el sıkışmasındaki ince detayları okuyarak trafiğin gerçek bir tarayıcıdan mı yoksa bir Python script'inden mi geldiğini tespit ediyor. curl_cffi ile TLS taklidi, bu el sıkışmasını Chrome veya Firefox'un BoringSSL/NSS yığınıyla birebir aynı şekilde sunarak ilk paket seviyesinde tarayıcı gibi görünmenizi sağlar.
Bu rehber, Python scraping mühendisleri ve anti-bot araştırmacıları için yazıldı. Amacımız, JA3 ve JA4 parmak izlerinin nasıl oluştuğunu, curl_cffi'nin bu parmak izlerini nasıl taklit ettiğini ve neden bunu tek başına kullanmanın yeterli olmadığını — yani residential proxy'lerin hala zorunlu olduğunu — açıklamak. Ayrıca ProxyHat residential çıkışlarıyla entegre, çalıştırılabilir bir örnek de paylaşacağız.
Bu içerik, yetkili güvenlik araştırmaları ve meşru otomasyon bağlamında hazırlanmıştır. Yalnızca herkese açık verilere ve erişimine izin verilmiş sistemlere erişim içindir.
Python requests ve urllib3 Neden Hemen Yakalanıyor?
Bir Python geliştiricisi olarak requests veya urllib3 ile ilk istediğinizi attığınızda, TLS el sıkışması OpenSSL üzerinden gerçekleşir. OpenSSL'in varsayılan ClientHello mesajı, Chrome'un BoringSSL yığınından belirgin şekilde farklıdır ve bu fark, ilk paketin kablosu üzerinden bile tespit edilebilir.
JA3 Parmak İzi Nasıl Oluşur?
JA4/JA3 spesifikasyonuna göre JA3, TLS ClientHello'daki üç alanın birleştirilip MD5 ile hash'lenmesiyle oluşur:
- TLSVersion — ClientHello'daki legacy_version alanı (genellikle 0x0303 = TLS 1.2)
- Ciphers — Sunulan cipher suite'lerinin sıralı listesi
- Extensions — TLS extension'larının sunuluş sırası
- Elliptic Curves — supported_groups extension'ındaki eğriler
- EC Point Formats — elliptic_curves extension'ındaki nokta formatları
Python requests kullandığınızda ortaya çıkan JA3 hash'i tipik olarak 771,4865-4866-4867-49195-49199-... gibi bir dize üretir. Chrome'un JA3 hash'i ise tamamen farklı bir cipher sıralamasına ve extension düzenine sahiptir. Anti-bot sistemleri bu hash'i bilinen tarayıcı imzalarıyla karşılaştırır ve eşleşme yoksa, henüz HTTP isteği göndermeden önce sizi bot olarak işaretler.
Cipher Sıralaması ve Extension Düzeni
OpenSSL, cipher suite'lerini genellikle güç sırasına göre sunar: önce ECDHE-ECDSA, sonra ECDHE-RSA, ardından TLS 1.3 cipher'ları. Chrome ise RFC 8446'ya uygun olarak TLS 1.3 cipher'larını (4865, 4866, 4867) öne koyar ve ardından GREASE değerlerini (0x0a0a, 0x1a1a gibi) rastgele konumlandırır. Bu GREASE değerleri, Chrome'un TLS yığınına gömülüdür ve OpenSSL'de varsayılan olarak bulunmaz.
Extension sıralaması daha da kritik. Chrome, server_name (SNI) extension'ını genellikle ilk sıraya koyar, ardından extended_master_secret, renegotiation_info, supported_groups, ec_point_formats ve session_ticket extension'larını belirli bir düzende sunar. OpenSSL ise farklı bir sıra kullanır — örneğin ec_point_formats'i supported_groups'tan önce sunabilir. Bu sıra farkı, JA3 hash'ini tamamen değiştirir.
GREASE ve TLS 1.3 ClientHello Şekli
GREASE (RFC 8701), Google'ın TLS extension'larına ve cipher listelerine bilinçli olarak eklediği sahte/değerlendirilemez değerlerdir. Amaç, ara katmanların bilinmeyen değerlerle nasıl başa çıktığını test etmektir. Chrome her bağlantıda GREASE değerlerini rastgele konumlandırır, bu da JA3 hash'inin her bağlantıda hafifçe değişmesine neden olur. OpenSSL bu davranışı taklit etmediği için, GREASE içermeyen bir ClientHello anında "tarayıcı değil" sinyali verir.
TLS 1.3 ClientHello'nun genel şekli de önemli. Chrome, supported_versions extension'ında TLS 1.3'ü belirtir ve key_share extension'ında x25519 ve P-256 için önceden hesaplanmış anahtar paylaşımları sunar. OpenSSL bu alanları farklı sıralarda veya farklı boyutlarda sunabilir. Anti-bot sistemleri bu yapısal farkları bile okuyabilir.
curl-impersonate ve curl_cffi Nasıl Çalışır?
curl-impersonate projesi, libcurl'u BoringSSL ile yeniden derleyerek ve Chrome'un TLS yığın yapılandırmasını enjekte ederek bu sorunu çözer. curl_cffi ise bu yeteneği Python'a taşır ve CFFI aracılığıyla doğrudan BoringSSL yığınına erişim sağlar. Sonuç: Python'dan Chrome'un birebir TLS parmak izini üretebilen bir HTTP istemcisi.
BoringSSL ve Chrome Cipher'larının Enjekte Edilmesi
curl_cffi, standart OpenSSL yerine Google'ın BoringSSL çatalını kullanır. BoringSSL, Chrome'un da kullandığı TLS yığıdır ve GREASE davranışını, cipher sıralamasını ve extension düzenini doğal olarak içerir. curl_cffi, BoringSSL'in yapılandırmasını Chrome'un belirli sürümleri için önceden ayarlanmış profillerle yükler.
Bu, yalnızca cipher listesini değiştirmekten ibaret değildir. curl_cffi aynı zamanda HTTP/2 SETTINGS frame'inin içeriğini ve sırasını da taklit eder. Chrome, HTTP/2 bağlantısında belirli SETTINGS değerlerini (HEADER_TABLE_SIZE=65536, ENABLE_PUSH=0, INITIAL_WINDOW_SIZE=6291456 gibi) belirli bir düzende gönderir. Anti-bot sistemleri bu SETTINGS frame'ini de okur ve standart Python httpx veya aiohttp istemcilerinin gönderdiği frame'den farklı olduğunu tespit eder.
impersonate="chrome" Preset'leri
curl_cffi'nin en güçlü özelliği, impersonate parametresidir. Bu parametre, önceden tanımlanmış tarayıcı profillerini yükler:
from curl_cffi import requests
# Chrome 120 parmak izi ile istek
r = requests.get(
"https://example.com",
impersonate="chrome120"
)
print(r.status_code)
Mevcut preset'ler arasında chrome99, chrome100, chrome101, chrome104, chrome107, chrome110, chrome116, chrome119, chrome120, chrome124, chrome131, safari15_3, safari15_5, safari17, edge99, edge101 ve Firefox varyantları bulunur. Her preset, ilgili tarayıcı sürümünün o dönemdeki ClientHello yapılandırmasını birebir kopyalar.
ja3, akamai ve extra_fp Override'ları
Preset'ler yeterli olmadığında, curl_cffi manuel override'lara izin verir. ja3 parametresi ile doğrudan JA3 dizesini belirtebilirsiniz:
from curl_cffi import requests
custom_ja3 = "771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,0-23-65281-10-11-35-16-5-13-18-51-45-43-27-17513,29-23-24,0"
r = requests.get(
"https://example.com",
ja3=custom_ja3
)
akamai parametresi ise HTTP/2 SETTINGS frame'ini ve pseudo-header sıralamasını manuel olarak ayarlamanızı sağlar. extra_fp parametresi ile TLS extension'larının sırasını, supported_groups listesini ve signature_algorithms listesini ince ayar yapabilirsiniz. Bu, özellikle anti-bot sistemlerinin belirli bir tarayıcı sürümünü engellediği durumlarda değerlidir.
Chrome 110+ ClientHello Permütasyonu ve JA4'ün Tasarımı
Chrome 110 sürümünden itibaren Google, ClientHello'daki TLS extension'larının sırasını her bağlantıda rastgele permüte etmeye başladı. Bu, JA3 hash'inin her bağlantıda değişmesine neden oldu ve JA3 tabanlı anti-bot sistemlerini zorladı. Eğer bir anti-bot sağlayıcı "Chrome 120'nin JA3 hash'i şudur" diyorsa, artık bu tek bir hash değil, binlerce olası hash'ten biridir.
Bu rastgele permütasyon, güvenlik araştırmacıları için yeni bir sorun yarattı: JA3 artık tek bir tarayıcı sürümünü güvenilir şekilde tanımlayamıyordu. İşte bu nedenle JA4 parmak izi tasarlandı.
JA4 Neden Sıra-Stabil?
JA4, JA3'ün aksine extension ve cipher listelerini alfabetik olarak sıralar ve hash'lemeden önce bunları birleştirir. Bu sayede, Chrome extension sıralamasını rastgele değiştirse bile, JA4 hash'i aynı kalır. JA4'ün formatı şu şekildedir:
JA4 = t13d1516h2_8daaf6152771_b186095e22b6
# t = TLS version (t13 = TLS 1.3)
# d = SNI var/yok (d = domain present)
# 1516 = 15 cipher + 16 extension sayısı
# h2 = ALPN protocol
# İlk alt çizgiden sonra: sıralanmış cipher hash'i
# İkinci alt çizgiden sonra: sıralanmış extension hash'i
Bu tasarım, JA4'ü Chrome'un permütasyonuna karşı dirençli kılar. Anti-bot sistemleri artık JA4'e geçiş yapıyor ve curl_cffi'nin impersonate="chrome131" preset'i, JA4 açısından da gerçek Chrome 131 ile eşleşecek şekilde tasarlanmıştır.
Neden Residential Proxy'ler Hala Zorunlu?
Buraya kadar olan kısım teknik olarak doğru: curl_cffi ile TLS parmak izinizi Chrome ile birebir eşleştirebilirsiniz. Ama bu, tek başına yeterli değildir. Anti-bot sistemleri çok katmanlı çalışır ve TLS parmak izi yalnızca bir katmandır.
IP İtibar Puanlaması
Cloudflare ve benzeri sistemler, gelen bağlantının IP adresini itibar veritabanlarında sorgular. Bir veri merkezi IP'si (ASN'si AWS, Google Cloud, DigitalOcean, Hetzner vb. olan) otomatik olarak daha yüksek risk puanı alır. Mükemmel bir TLS parmak izi bile, veri merkezi IP'si üzerinden geldiğinde itibar puanlamasında başarısız olur.
Bunun nedeni istatistiksel: gerçek kullanıcıların %99'u ISP (Internet Service Provider) IP'lerinden gelir. Veri merkezi IP'lerinden gelen trafik, bot olasılığı açısından çok daha yüksek risk taşır. Anti-bot sistemleri bu öncelikle IP itibarına, ardından TLS parmak izine bakar.
| Özellik | Datacenter Proxy | Residential Proxy |
|---|---|---|
| IP ASN tipi | Cloud / Hosting sağlayıcı | Gerçek ISP (Deutsche Telekom, Comcast vb.) |
| İtibar puanı | Düşük (yüksek risk) | Yüksek (düşük risk) |
| TLS parmak izi ile uyum | Yetersiz — IP itibarı düşük | Güçlü — IP + TLS birlikte tutarlı |
| Coğrafi hedefleme | Sınırlı | Ülke ve şehir seviyesi |
| Anti-bot atlatma başarısı | %30-50 (tahmini) | %85-95 (tahmini) |
ProxyHat'ın proxy lokasyonları arasında 190+ ülke bulunur ve residential proxy'ler gerçek ISP IP'lerinden gelir. Bu, TLS parmak izinizin tutarlı bir IP itibarıyla desteklenmesini sağlar.
Çok Katmanlı Doğrulama
Modern anti-bot sistemleri şu katmanları sırayla kontrol eder:
- IP itibarı — ASN, geo, blacklist, historical abuse
- TLS parmak izi — JA3/JA4, cipher order, extensions
- HTTP/2 parmak izi — SETTINGS frame, pseudo-header order, window size
- HTTP başlık düzeni — Accept-Language, Accept-Encoding, sec-ch-ua sıralaması
- Davranışsal analiz — istek hızı, fare hareketi, scroll pattern (JS gerektirir)
curl_cffi ilk üç katmanı çözer. HTTP başlık düzenini manuel olarak ayarlamanız gerekir. Davranışsal analiz ise curl_cffi'nin kapsamı dışındadır — bu, gerçek bir tarayıcı (Playwright, Puppeteer) gerektirir.
Çalıştırılabilir Örnek: curl_cffi + ProxyHat Residential Proxy
Şimdi tüm bunları pratike dökelim. Aşağıdaki örnek, curl_cffi'nin AsyncSession'ını ProxyHat residential proxy'leri üzerinden çalıştırır. Almanya çıkışlı (-country-DE) bir proxy kullanıyoruz ve Chrome 131 parmak izini taklit ediyoruz.
Kurulum
pip install curl_cffi
pip install proxyhat # ProxyHat Python SDK (opsiyonel)
Temel curl_cffi Örneği
import asyncio
from curl_cffi.requests import AsyncSession
PROXY_URL = "http://user-country-DE:password@gate.proxyhat.com:8080"
async def fetch_with_tls_impersonation():
async with AsyncSession(
impersonate="chrome131",
proxies={"http": PROXY_URL, "https": PROXY_URL}
) as session:
r = await session.get(
"https://www.cloudflare.com/cdn-cgi/trace",
timeout=30
)
print(f"Status: {r.status_code}")
print(f"Body:\n{r.text}")
# JA3/JA4 parmak izini doğrulamak için:
# https://tls.browserleaks.com/json üzerinden kontrol edebilirsiniz
asyncio.run(fetch_with_tls_impersonation())
Bu kodda dikkat etmeniz gereken birkaç nokta:
impersonate="chrome131"— Chrome 131'in TLS ve HTTP/2 parmak izini yüklerproxiesparametresi — hem HTTP hem HTTPS trafiğini ProxyHat gateway'ine yönlendiriruser-country-DE— Almanya residential çıkışını belirtir
Rotasyon ve Retry ile Gelişmiş Örnek
Tek bir IP ile yüksek hacimli scraping yaparsanız, hedef site sizi hız limiting'e takabilir. ProxyHat, her istekte yeni bir IP almanızı sağlar. İşte rotasyon ve retry mantığı içeren daha sağlam bir örnek:
import asyncio
import random
from curl_cffi.requests import AsyncSession
# ProxyHat gateway bilgileri
GATEWAY = "gate.proxyhat.com"
PORT = 8080
USERNAME = "user-country-DE"
PASSWORD = "your_password"
# Her oturum için benzersiz session ID → sticky IP
# Session ID olmadan → her istekte yeni IP
def build_proxy_url(session_id=None):
user = USERNAME
if session_id:
user = f"{USERNAME}-session-{session_id}"
return f"http://{user}:{PASSWORD}@{GATEWAY}:{PORT}"
async def fetch_with_retry(url, max_retries=3):
for attempt in range(max_retries):
# Her denemede yeni bir IP (session ID yok)
proxy_url = build_proxy_url()
try:
async with AsyncSession(
impersonate="chrome131",
proxies={"http": proxy_url, "https": proxy_url}
) as session:
r = await session.get(url, timeout=30)
if r.status_code == 200:
return r
elif r.status_code == 429:
# Rate limited — bekle ve yeniden dene
wait = 2 ** attempt + random.uniform(0, 1)
print(f"429 received, waiting {wait:.1f}s")
await asyncio.sleep(wait)
continue
else:
print(f"Status {r.status_code}, retrying...")
except Exception as e:
print(f"Attempt {attempt+1} failed: {e}")
await asyncio.sleep(1)
return None
async def main():
urls = [
"https://httpbin.org/headers",
"https://httpbin.org/ip",
"https://tls.browserleaks.com/json",
]
tasks = [fetch_with_retry(url) for url in urls]
results = await asyncio.gather(*tasks)
for url, r in zip(urls, results):
if r:
print(f"\n=== {url} ===")
print(r.text[:500])
asyncio.run(main())
SOCKS5 ile Daha Düşük Gecikme
Eğer HTTP proxy'den daha düşük gecikme istiyorsanız, ProxyHat'ın SOCKS5 portunu kullanabilirsiniz:
SOCKS5_PROXY = "socks5://user-country-DE:password@gate.proxyhat.com:1080"
async with AsyncSession(
impersonate="chrome131",
proxies={"http": SOCKS5_PROXY, "https": SOCKS5_PROXY}
) as session:
r = await session.get("https://example.com")
SOCKS5, HTTP CONNECT tüneline kıyasla yaklaşık 50-100ms daha düşük gecikme sunabilir, ancak bazı hedef siteler SOCKS5 trafiğini farklı şekilde ele alabilir. İhtiyacınıza göre test edin.
curl_cffi ile İlgili Yaygın Hatalar ve Edge Case'ler
1. User-Agent ile TLS Parmak İzi Uyumsuzluğu
En sık yapılan hata: impersonate="chrome131" kullanırken User-Agent başlığını Firefox veya Safari olarak ayarlamak. Anti-bot sistemleri TLS parmak izi ile User-Agent başlığını çapraz kontrol eder. Eğer TLS parmak iziniz Chrome 131'i söylüyorsa ama User-Agent Firefox 120'yi söylüyorsa, anında tutarsızlık tespit edilir.
# YANLIŞ — TLS Chrome, UA Firefox
r = requests.get(url, impersonate="chrome131",
headers={"User-Agent": "Mozilla/5.0 ... Firefox/120"})
# DOĞRU — TLS ve UA aynı tarayıcı
r = requests.get(url, impersonate="chrome131")
# curl_cffi otomatik olarak Chrome 131 UA'sını gönderir
2. Accept-Language ve sec-ch-ua Başlıklarını Atlamak
Chrome, sec-ch-ua, sec-ch-ua-mobile, sec-ch-ua-platform gibi Client Hints başlıklarını gönderir. curl_cffi preset'leri bu başlıkları otomatik ekler, ancak kendi başlıklarınızı geçersiz kılmak bunları silebilir. Başlık override yaparken Client Hints başlıklarını koruyun.
3. HTTP/1.1'e Düşmek
Bazı proxy yapılandırmaları, HTTP/2'yi desteklemeyebilir ve bağlantı HTTP/1.1'e düşebilir. Bu durumda HTTP/2 SETTINGS frame parmak izi kaybolur. Anti-bot sistemleri "Chrome ama HTTP/1.1" bağlantısını şüpheli bulur. ProxyHat gateway'i HTTP/2'yi destekler, ancak yerel test ortamınızda bunu doğrulayın.
4. Eski curl_cffi Sürümleri
curl_cffi'nin eski sürümleri (0.5.x öncesi), Chrome preset'lerinin bir kısmını desteklemeyebilir. Her zaman en son sürümü kullanın: pip install --upgrade curl_cffi. 2026 itibarıyla en güncel preset'ler Chrome 131 ve Safari 17'yi içerir.
Sınırlamalar ve Etik Boyutlar
curl_cffi JS Challenge'ları Çözemez
curl_cffi, TLS ve HTTP/2 seviyesinde taklit yapar, ancak JavaScript çalıştırmaz. Cloudflare'in Turnstile'ı, Akamai'nin Bot Manager'ı veya Datadome'un JS challenge'ı, tarayıcıda JavaScript yürüterek canvas parmak izi, WebGL renderer bilgisi ve davranışsal sinyaller toplar. Bu challenge'ları geçmek için gerçek bir tarayıcı motoru gerekir — Playwright, Puppeteer veya Selenium.
Bu durumda, curl_cffi'yi ilk istek için (örneğin cookie ve token toplamak için) kullanabilir, ardından bu cookie'leri Playwright oturumuna aktarabilirsiniz. Veya ProxyHat residential proxy'lerini doğrudan Playwright ile kullanabilirsiniz.
Meşru Erişim ve Yasal Çerçeve
TLS taklidi ve proxy kullanımı, teknik olarak nötr araçlardır. Ancak bu araçları ne için kullandığınız önemlidir. ABD'de Computer Fraud and Abuse Act (CFAA), yetkisiz erişimi suç sayar. AB'de GDPR, kişisel verilerin işlenmesini sıkı kurallara bağlar. Herkese açık verilerin toplanması genellikle meşru kabul edilir, ancak:
- Hedef sitenin
robots.txtdosyasını kontrol edin ve saygı gösterin - Hizmet şartlarını (ToS) okuyun — bazı siteler scraping'i açıkça yasaklar
- Kişisel veri topluyorsanız GDPR/CCPA uyumluluğunu sağlayın
- Rate limiting'e saygı gösterin — aşırı istek hedef siteyi etkileyebilir
- Yalnızca erişimine izin verilmiş verilere ve sistemlere erişin
Güvenlik araştırmacıları için: yetkili penetrasyon testi bağlamında TLS taklidi, savunma mekanizmalarını test etmek için meşru bir tekniktir. Hedef sistemin sahibinden yazılı onay aldığınızdan emin olun.
Key Takeaways
- TLS parmak izi ilk pakette oluşur — JA3/JA4, cipher sıralaması, extension düzeni ve GREASE değerleri, anti-bot sistemleri tarafından ilk pakette okunur.
- curl_cffi, BoringSSL ile Chrome'u birebilir taklit eder —
impersonate="chrome131"preset'i TLS, HTTP/2 ve başlık düzeyinde Chrome 131 ile eşleşir. - JA4, Chrome 110+ permütasyonuna karşı sıra-stabil tasarlanmıştır — extension'ları alfabetik sıralayarak hash'i tutarlı tutar.
- Residential proxy'ler zorunludur — mükemmel TLS parmak izi bile veri merkezi IP'si üzerinden geldiğinde itibar puanlamasında başarısız olur. ProxyHat residential proxy'leri gerçek ISP IP'leri sağlar.
- curl_cffi JS challenge'ları çözemez — Cloudflare Turnstile, Akamai Bot Manager gibi JS tabanlı challenge'lar için gerçek tarayıcı (Playwright/Puppeteer) gerekir.
- Etik sınırlara saygı gösterin — robots.txt, ToS, GDPR ve CFAA çerçevesinde, yalnızca yetkili ve herkese açık verilere erişin.
ProxyHat'ın fiyatlandırma seçeneklerini inceleyebilir veya web scraping use case ve SERP tracking sayfalarımızdan daha fazla bilgi alabilirsiniz. Teknik dokümantasyon için ProxyHat Docs'a bakın.






