---
title: "TLS Parmak İzi ve JA3 Nedir, Nasıl Çalışır?"
description: "TLS parmak izi, istemcinin ClientHello paketindeki şifre ve uzantı listesinden çıkarılan kimliktir. JA3 ve JA4 hesabını, proxy'nin buna etkisini anlatıyoruz."
url: https://proxynet.io/tr/blog/tls-fingerprinting
date: 2026-09-19
author: "Acar Diveroli"
category: "Web Scraping, Proxy 101"
lang: tr
---

# TLS Parmak İzi ve JA3 Nedir, Nasıl Çalışır?

Python ile yazdığınız bir betik var. Proxy üzerinden çıkıyor, IP adresi her istekte değişiyor, `User-Agent` başlığına güncel bir Chrome satırı yazdınız. Buna rağmen site daha ilk istekte `403` dönüyor. Aynı adresi aynı IP ile gerçek bir tarayıcıda açtığınızda ise sayfa geliyor. Aradaki fark, HTTP başlıklarından önce giden bir pakettedir: şifreli bağlantıyı başlatan `ClientHello` mesajı. Site bu mesajın düzenine bakarak karşısındakinin Chrome mu, Python mu, curl mü olduğunu başlıkları okumadan anlayabilir.

Bu yazıda TLS el sıkışmasını, `ClientHello` mesajındaki alanları, JA3 dizgisinin bu alanlardan nasıl hesaplandığını ve JA4'ün neyi farklı yaptığını anlatıyoruz. Ardından en çok sorulan soruya geçiyoruz: proxy TLS parmak izini değiştirir mi? Sonda kendi istemcinizin JA3 dizgisini yerelde gösteren, denenmiş bir Python örneği var.

> **Not: Kısa cevap**
>
> TLS parmak izi, bir istemcinin şifreli bağlantı kurarken gönderdiği `ClientHello` mesajındaki sürüm, şifre takımı, uzantı ve eğri listelerinden üretilen bir özettir. JA3 bu listeleri sırasıyla birleştirip MD5 özetini alır; JA4 listeleri sıralar ve okunabilir bir ön ek ekler. Değer kişiyi değil **yazılımı** tanımlar: aynı sürümdeki bütün Chrome'lar aynı parmak izini taşır. HTTP `CONNECT` tüneli ve SOCKS5 proxy bu mesaja dokunmaz, yani proxy IP'yi değiştirir, TLS parmak izini değiştirmez.

## TLS el sıkışması nedir?

TLS, tarayıcı ile site arasındaki trafiği şifreleyen protokoldür; adres çubuğundaki `https://` onu kullandığınızı gösterir. Şifreleme başlamadan önce iki tarafın hangi sürümü, hangi şifre takımını ve hangi anahtarı kullanacağı konusunda anlaşması gerekir. Bu kısa pazarlığa el sıkışma (handshake) denir. Güncel sürüm olan TLS 1.3, [RFC 8446](https://www.rfc-editor.org/rfc/rfc8446#section-4.1.2) belgesinde tanımlıdır ve el sıkışma kabaca şöyle ilerler:

1. İstemci sunucuya TCP ile bağlanır ve bir `ClientHello` mesajı gönderir. Mesajda desteklediği şifre takımları, uzantılar ve anahtar paylaşımı bulunur.
2. Sunucu listeden birini seçer ve `ServerHello` ile yanıtlar. Bu noktadan sonra iki taraf ortak anahtarı türetebilir.
3. Sunucu sertifikasını ve el sıkışmanın bütünlüğünü kanıtlayan `Finished` mesajını şifreli olarak gönderir.
4. İstemci sertifikayı doğrular, kendi `Finished` mesajını yollar.
5. İlk HTTP isteği (`GET /`, başlıklar, `User-Agent`) ancak bundan sonra, şifreli kanalın içinden gider.

Parmak izi açısından önemli olan birinci adımdır. `ClientHello` henüz ortak anahtar yokken gönderildiği için **şifresizdir**; sunucu, önündeki CDN ve yol üzerindeki her ağ cihazı onu başlıklardan, çerezlerden ve JavaScript'ten önce olduğu gibi okuyabilir.

## ClientHello mesajında hangi alanlar var?

`ClientHello`, istemcinin "şunları konuşabiliyorum" dediği bir listedir. Parmak izinde kullanılan alanlar şunlardır:

| Alan | Ne taşır? | Neden ayırt edici? |
|---|---|---|
| `legacy_version` | Eski sürüm alanı. TLS 1.3 istemcileri uyumluluk için burada hâlâ TLS 1.2 değerini (`771`) yazar | Tek başına az bilgi taşır; gerçek sürüm uzantıdadır |
| `cipher_suites` | Desteklenen şifre takımları, **tercih sırasıyla** | Hem liste hem sıra kütüphaneden kütüphaneye değişir |
| `extensions` | Uzantıların tür numaraları: `server_name` (0), `supported_groups` (10), `signature_algorithms` (13), ALPN (16), `supported_versions` (43), `key_share` (51) ve diğerleri | Hangi uzantıların bulunduğu ve sırası yazılıma özgüdür |
| `supported_groups` | Anahtar değişiminde kullanılabilecek eğriler ve gruplar (`29` = x25519, `23` = secp256r1) | Yeni gruplar önce tarayıcılara gelir |
| `ec_point_formats` | Eğri noktası biçimleri | Bazı kütüphaneler üç değer, bazıları tek değer gönderir |
| `signature_algorithms` | Kabul edilen imza algoritmaları, sırayla | JA3 kullanmaz, JA4 kullanır |
| ALPN | İstemcinin konuşmak istediği uygulama protokolü (`h2`, `http/1.1`) | Tarayıcı `h2` ister, basit istemciler çoğu zaman istemez |

Bu listeleri uygulamanız değil, altındaki **TLS kütüphanesi** doldurur. Chrome BoringSSL, Firefox NSS kullanır. Python'un `ssl` modülü ve Node.js OpenSSL üzerine kuruludur. Windows ile gelen curl, işletim sisteminin kendi TLS yığını olan Schannel'i kullanır. Her kütüphanenin varsayılan şifre listesi, uzantı seti ve sıralaması farklıdır; istemcinin kimliği böylece el sıkışmanın ilk paketine sızar.

## TLS parmak izi nedir?

TLS parmak izi, `ClientHello` mesajındaki bu listelerin kısa ve karşılaştırılabilir bir dizgiye indirgenmiş hâlidir. Üç özelliği onu diğer sinyallerden ayırır:

- **Pasiftir.** Site istemciye hiçbir şey çalıştırtmaz, yalnızca gelen ilk paketi okur. JavaScript'i kapatmak ya da çerezleri silmek sonucu etkilemez.
- **Başlıklardan bağımsızdır.** `User-Agent` bir metin satırıdır ve her HTTP kütüphanesinde tek satırla değişir. `ClientHello` ise kütüphanenin derlenmiş davranışından gelir.
- **Kişiyi değil yazılımı tanımlar.** Aynı işletim sisteminde aynı Chrome sürümünü kullanan herkes aynı değeri üretir. Cihazı tek tek ayırt etmeyi hedefleyen JavaScript katmanını (canvas, font, WebGL) [Tarayıcı Parmak İzi Nedir?](/tr/blog/browser-fingerprinting) yazımızda anlattık.

Yöntem bot tespiti için icat edilmedi. İlk kullanım alanı ağ güvenliğiydi: zararlı yazılım trafiği şifreler, ama TLS kütüphanesi ve ayarları çoğu zaman sabittir. İçeriği göremeyen güvenlik ekibi, aynı yazılım ailesini `ClientHello` düzeninden tanıyabilir.

## JA3 nedir ve nasıl hesaplanır?

JA3, bu fikri standart bir biçime bağlayan ilk yaygın yöntemdir. 2017'de Salesforce'ta John Althouse, Jeff Atkinson ve Josh Atkins tarafından geliştirildi ve açık kaynak olarak yayımlandı. Hesap, [Salesforce'un JA3 deposunda](https://github.com/salesforce/ja3) anlatıldığı gibi beş adımdan oluşur:

1. `ClientHello` mesajından beş alan alınır: TLS sürümü, şifre takımları, uzantılar, eğriler (`supported_groups`) ve eğri noktası biçimleri.
2. Her alandaki değerler **onluk sayıya** çevrilir ve mesajdaki sırasıyla `-` ile birleştirilir.
3. Beş alan `,` ile birleştirilir. Bir alan boşsa yeri boş bırakılır.
4. GREASE değerleri (aşağıda anlatıyoruz) listeye hiç alınmaz.
5. Ortaya çıkan dizginin MD5 özeti alınır. 32 karakterlik bu özet JA3 parmak izidir.

Deponun kendi örneği şöyledir:

```text
769,47-53-5-10-49161-49162-49171-49172-50-56-19-4,0-10-11,23-24-25,0
→ ada70206e40642a3e4461f35503241d5
```

Soldan okuyunca: `769` TLS 1.0 demektir; ardından on iki şifre takımı, üç uzantı, üç eğri ve tek bir nokta biçimi gelir. MD5 burada güvenlik için değil, uzun dizgiyi sabit boyda aranabilir bir anahtara çevirmek için kullanılır.

İki not: TLS 1.3 istemcileri sürüm alanına TLS 1.2 değerini yazdığı için güncel JA3 dizgilerinin neredeyse hepsi `771` ile başlar. Salesforce depoyu 1 Mayıs 2025'te arşivledi; Wireshark gibi araçlar değeri hesaplamayı sürdürüyor, ama yöntem artık bakım görmüyor.

## GREASE nedir, JA3 onu neden yok sayar?

GREASE, [RFC 8701](https://www.rfc-editor.org/rfc/rfc8701) ile tanımlanan bir dayanıklılık mekanizmasıdır. İstemci; şifre takımı, uzantı ve grup listelerine `0x0A0A`, `0x1A1A` … `0xFAFA` kalıbındaki ayrılmış değerlerden rastgele birkaçını ekler. Bu değerlerin hiçbir anlamı yoktur. Amaç, sunucuların tanımadıkları değerleri sessizce yok sayıp saymadığını sürekli sınamaktır: tanımadığı değerde bağlantıyı kesen hatalı bir sunucu, yeni bir TLS özelliği çıktığında değil, bugün fark edilir.

Değerler rastgele seçildiği için JA3 bunları hesaba katsaydı aynı tarayıcı her bağlantıda farklı bir özet üretirdi. Yöntemin belgesi bu yüzden GREASE değerlerinin atlanmasını ister; aşağıdaki kodda bunu `is_grease` fonksiyonu yapıyor.

## Chrome uzantı sırasını neden karıştırıyor?

GREASE listelerin içeriğini korur, sırasını korumaz. Sunucu ve ara cihaz yazılımlarının Chrome'un sabit **uzantı sırasına** güvenmesi aynı riski taşıyordu; Chrome ekibi sırayı da rastgele hâle getirdi. [Chrome Platform Status'taki kayda](https://chromestatus.com/feature/5124606246518784) göre "TLS ClientHello extension permutation" özelliği Chrome 110 ile varsayılan olarak açıldı. Kayıttaki gerekçe parmak izinden kaçmak değil, ekosistemin kırılganlaşmasını önlemektir: sabit bir sıra, sunucu geliştiricilerini Chrome'u tanıyıp belirli bir davranış varsaymaya iter ve ileride TLS'e yapılacak değişiklikleri zorlaştırır. RFC 8446 zaten uzantıların herhangi bir sırada gelebileceğini söyler; tek istisna, varsa en sonda durması gereken `pre_shared_key` uzantısıdır.

JA3 uzantıları mesajdaki sırasıyla birleştirdiği için bu değişiklik Chromium tabanlı tarayıcıların JA3 özetini kararsız kıldı. Aşağıdaki dinleyiciyle ölçtük: Chromium tabanlı bir tarayıcının art arda açtığı 24 bağlantıdan **24 farklı JA3 özeti** çıktı. Uzantı numaralarını sıraladığımızda ise hepsinde aynı küme vardı; değişen yalnızca sıraydı.

## JA4 nedir, JA3'ten farkı ne?

JA4, FoxIO'nun yayımladığı ve bu soruna cevap veren yeni nesil biçimdir. [JA4 teknik belgesine](https://github.com/FoxIO-LLC/ja4/blob/main/technical_details/JA4.md) göre parmak izi `a_b_c` biçiminde üç parçadan oluşur. Belgedeki örnek `t13d1516h2_8daaf6152771_e5627efa2ab1` şöyle okunur:

- **`t`**: TCP üzerinden TLS (`q` QUIC, `d` DTLS olurdu).
- **`13`**: TLS 1.3. JA4 sürümü eski alandan değil `supported_versions` uzantısından okur.
- **`d`**: SNI uzantısı var, yani istemci bir alan adına bağlanıyor (`i`, SNI'ın bulunmadığını gösterir).
- **`15`** ve **`16`**: GREASE hariç 15 şifre takımı ve 16 uzantı.
- **`h2`**: ALPN listesindeki ilk değerin ilk ve son karakteri, yani HTTP/2.
- **`8daaf6152771`**: şifre takımlarının onaltılık kodları **sıralanır**, SHA-256 özeti alınır, ilk 12 karakter tutulur.
- **`e5627efa2ab1`**: uzantı kodları sıralanır (SNI ve ALPN hariç), sonuna imza algoritmaları özgün sırasıyla eklenir, aynı şekilde özetlenir.

Sıralama, uzantı karıştırmanın etkisini ortadan kaldırır. Okunabilir ilk parça ise özetlere bakmadan kaba bir ayrım yapmaya izin verir: biri `h2`, diğeri ALPN göndermediği için `00` ile biten iki ilk parça aynı yazılımdan gelmiyordur.

| | JA3 | JA4 |
|---|---|---|
| Yayımlayan | Salesforce (2017), depo 2025'te arşivlendi | FoxIO, geliştirme sürüyor |
| Biçim | 32 karakterlik tek MD5 özeti | `a_b_c`: okunabilir ön ek + iki kısaltılmış SHA-256 |
| Uzantı sırası | Mesajdaki sıra | Sıralanmış; karıştırmadan etkilenmez |
| TLS sürümü | Eski sürüm alanı (TLS 1.3'te de `771`) | `supported_versions` uzantısı |
| ALPN, SNI | Yalnızca uzantı numarası olarak görünür | İkisi de ilk parçada açıkça yazılır |
| İmza algoritmaları | Kullanılmaz | Üçüncü parçaya girer |
| GREASE | Yok sayılır | Yok sayılır |

FoxIO'nun deposundaki lisans notuna göre TLS istemci parmak izi olan JA4, BSD 3-Clause lisansıyla açıktır; ailenin diğer üyeleri (JA4S, JA4H, JA4X, JA4T ve devamı) ayrı bir FoxIO lisansına bağlıdır.

## Proxy TLS parmak izini değiştirir mi?

Hayır. Bunun nedeni proxy'nin HTTPS trafiğini nasıl taşıdığında yatar.

HTTP proxy'de istemci önce proxy'ye `CONNECT hedef.com:443` isteği gönderir. Proxy hedefe bir TCP bağlantısı açar, `200 Connection Established` der ve o andan sonra yalnızca bayt aktarır. `ClientHello` bu tünelin **içinden** geçer: onu sizin istemciniz üretir, hedef site okur, proxy ise ne içeriğini değiştirir ne de yeniden yazar. SOCKS5'te durum aynıdır; protokol TCP bağlantısını bir katman aşağıda aktarır ve taşıdığı verinin TLS olduğunu bilmez bile. İki protokolün adım adım akışını [SOCKS ve HTTP Proxy Farkı](/tr/blog/socks-vs-http-proxy) yazımızda anlattık.

Sonuç şudur: site, proxy'nin IP adresini ve **sizin istemcinizin** TLS parmak izini aynı anda görür. [HTTPS Proxy](https://proxynet.io/tr/https-proxy) ya da [SOCKS5 Proxy](https://proxynet.io/tr/socks5-proxy) kullanmanız bunu değiştirmez; proxy'nin residential, mobil ya da datacenter olması da değiştirmez. Yerel bir test proxy'siyle denedik: aynı curl istemcisi aşağıdaki dinleyiciye doğrudan, HTTP `CONNECT` tüneliyle ve SOCKS5 üzerinden bağlandı, dinleyici üçünde de aynı JA3 dizgisini kaydetti.

Parmak izinin değiştiği tek durum, arada TLS'i **sonlandıran** bir aracının bulunmasıdır:

| Aradaki şey | TLS'i hedefle kim kurar? | Site hangi parmak izini görür? |
|---|---|---|
| HTTP proxy (`CONNECT` tüneli) | Sizin istemciniz | Sizin istemcinizinkini |
| SOCKS5 proxy | Sizin istemciniz | Sizin istemcinizinkini |
| VPN | Sizin istemciniz | Sizin istemcinizinkini |
| Kurumsal TLS denetimi yapan ağ geçidi | Ağ geçidi | Ağ geçidininkini |
| HTTPS taraması açık antivirüs | Antivirüs | Antivirüsünkünü |
| Sayfayı sizin adınıza çeken servis | Servisin kendi istemcisi | Servisin istemcisininkini |

Beşinci satırı bu yazıyı hazırlarken kendi makinemizde yaşadık: Windows'taki curl, yankı servisine doğrudan bağlandığında başka, yerel proxy tünelinden geçtiğinde başka bir parmak izi döndü. Nedeni antivirüsün HTTPS taramasıydı; doğrudan bağlantıyı kendi TLS yığınıyla yeniden kuruyordu. Yankı servisindeki değer beklediğiniz kütüphaneye benzemiyorsa sertifikayı kimin imzaladığına bakın.

## Kendi JA3 dizginizi nasıl görürsünüz?

En kısa yol bir yankı servisidir: tarayıcıda [BrowserLeaks'in TLS sayfasını](https://browserleaks.com/tls) açtığınızda JA3 ve JA4 değerlerinizi görürsünüz. Paket düzeyinde bakmak isterseniz Wireshark `ClientHello` paketlerinde bu değerleri kendisi hesaplar; [görüntüleme filtresi başvurusunda](https://www.wireshark.org/docs/dfref/t/tls.html) alanlar `tls.handshake.ja3` ve `tls.handshake.ja4` adlarıyla listelenir.

Hesabın kendisini görmek için aşağıdaki betiği kullanabilirsiniz. Yalnızca Python'un standart kütüphanesiyle çalışır ve dışarıya istek göndermez: `8443` portunda ham bir TCP dinleyicisi açar, bağlanan her istemcinin `ClientHello` mesajını ayrıştırıp JA3 dizgisini ve özetini yazdırır. Açılışta Python'un kendi `urllib` istemcisini bir kez bağlar. Dinleyici el sıkışmayı tamamlamaz, ilk paketi okuyup kapatır; istemci tarafındaki bağlantı hatası beklenen bir durumdur.

```python
import hashlib
import socket
import threading
import urllib.request

HOST, PORT = "127.0.0.1", 8443

def is_grease(value):
    # RFC 8701: 0x0a0a, 0x1a1a, ... 0xfafa
    return (value & 0x0F0F) == 0x0A0A and (value >> 8) == (value & 0xFF)

def read_client_hello(conn):
    data = b""
    while len(data) < 5:
        data += conn.recv(4096)
    if data[0] != 22:  # 22 = handshake kaydı
        raise ValueError("TLS el sıkışması değil")
    record_length = int.from_bytes(data[3:5], "big")
    while len(data) < 5 + record_length:
        chunk = conn.recv(4096)
        if not chunk:
            break
        data += chunk
    return data[5 : 5 + record_length]

def ja3_from_client_hello(hello):
    if hello[0] != 1:  # 1 = ClientHello
        raise ValueError("ClientHello değil")
    pos = 4  # mesaj türü (1) + uzunluk (3)
    version = int.from_bytes(hello[pos : pos + 2], "big")
    pos += 2 + 32  # sürüm + random
    pos += 1 + hello[pos]  # session_id
    size = int.from_bytes(hello[pos : pos + 2], "big")
    pos += 2
    ciphers = [int.from_bytes(hello[i : i + 2], "big") for i in range(pos, pos + size, 2)]
    pos += size
    pos += 1 + hello[pos]  # compression_methods
    end = pos + 2 + int.from_bytes(hello[pos : pos + 2], "big")
    pos += 2

    extensions, groups, point_formats = [], [], []
    while pos < end:
        ext_type = int.from_bytes(hello[pos : pos + 2], "big")
        ext_size = int.from_bytes(hello[pos + 2 : pos + 4], "big")
        body = hello[pos + 4 : pos + 4 + ext_size]
        pos += 4 + ext_size
        extensions.append(ext_type)
        if ext_type == 10:  # supported_groups
            groups = [int.from_bytes(body[i : i + 2], "big") for i in range(2, len(body), 2)]
        elif ext_type == 11:  # ec_point_formats
            point_formats = list(body[1:])

    def join(values):
        return "-".join(str(v) for v in values if not is_grease(v))

    fields = [str(version), join(ciphers), join(extensions), join(groups), join(point_formats)]
    ja3_text = ",".join(fields)
    return ja3_text, hashlib.md5(ja3_text.encode()).hexdigest()

def own_python_client():
    try:
        urllib.request.urlopen(f"https://localhost:{PORT}", timeout=5)
    except OSError:
        pass  # dinleyici yanıt vermeden kapatır, beklenen durum

def main():
    server = socket.create_server((HOST, PORT))
    print(f"https://localhost:{PORT} dinleniyor, çıkmak için Ctrl+C")
    threading.Thread(target=own_python_client, daemon=True).start()
    while True:
        conn, _ = server.accept()
        with conn:
            try:
                ja3_text, ja3_hash = ja3_from_client_hello(read_client_hello(conn))
            except (ValueError, IndexError, OSError) as exc:
                print("okunamadı:", exc)
                continue
        print(ja3_hash, ja3_text)

if __name__ == "__main__":
    main()
```

Betik çalışırken başka bir terminalden farklı istemcileri aynı adrese yönlendirin. Adreste `localhost` yazın; IP adresine bağlanan istemci SNI uzantısını göndermez ve parmak izi değişir.

```bash
curl -k https://localhost:8443
node -e "require('https').get('https://localhost:8443', { rejectUnauthorized: false }).on('error', () => {})"
```

Bizim makinemizde (Windows 11, Python 3.13 ve OpenSSL 3.0, Node.js 24, Schannel ile derlenmiş curl 8) çıktı şöyleydi. Okunabilsin diye şifre takımı alanını kısalttık:

```text
331a436afb23d4e31134c11b301bdcb5 771,4866-4867-4865-…,0-11-10-35-16-22-23-49-13-43-45-51-21,29-23-30-25-24-256-257-258-259-260,0-1-2
2e6c64f66822fc35b6a7a128b557f1de 771,4866-4865-49196-…,0-43-13-35-10-11-16-51-49-23-65281-45,29-23-24,0
944d1e1858cd278718f8a46b65d3212f 771,4866-4867-4865-…,65281-0-11-10-35-22-23-13-43-45-51,4588-29-23-30-24-25-256-257,0-1-2
```

Aynı makine, aynı IP, üç ayrı kimlik: sırasıyla Python, curl ve Node.js. Python'un özetini yankı servisi de birebir aynı verdi, yani ayrıştırıcı doğru çalışıyor. Node.js satırındaki `4588`, IANA kayıtlarında `X25519MLKEM768` adıyla geçen kuantum sonrası hibrit anahtar değişimi grubudur; Python'un bu sürümü onu göndermiyor. Aynı Node.js'te `https.get` ile yerleşik `fetch` de farklı JA3 verdi, çünkü `fetch` ALPN uzantısını ekliyor: parmak izi dile değil, kullanılan HTTP kütüphanesine bağlıdır. Python tarafındaki kütüphane farklarını [HTTPX, Requests ve AIOHTTP](/tr/blog/httpx-vs-requests-vs-aiohttp) yazımızda karşılaştırdık.

Proxy'nin etkisini ölçmek için dinleyici işe yaramaz (uzaktaki proxy sizin `localhost` adresinize ulaşamaz), bu yüzden yankı servisine iki kez sorun:

```python
import json
import urllib.request

PROXY = "http://user:pass@pr.proxynet.io:8000"
URL = "https://tls.browserleaks.com/json"

def fingerprint(opener):
    with opener.open(URL, timeout=15) as response:
        result = json.load(response)
    return result["ja3_hash"], result["ja4"]

direct = urllib.request.build_opener(urllib.request.ProxyHandler({}))
proxied = urllib.request.build_opener(urllib.request.ProxyHandler({"https": PROXY}))

print("direct :", *fingerprint(direct))
print("proxied:", *fingerprint(proxied))
```

İki satırda aynı değerleri görürsünüz; değişen yalnızca servisin gördüğü IP adresidir.

## Site sahibi bu veriyi nasıl kullanır?

CDN ve güvenlik duvarı ürünleri JA3 ya da JA4 değerini her isteğin yanında bir alan olarak sunar. Site sahibi bu alanı kural yazarken ve günlükleri incelerken kullanır. Tipik kullanımlar:

- **Tutarlılık kontrolü.** `User-Agent` "Chrome" diyor ama parmak izi bilinen hiçbir Chrome sürümüne uymuyorsa, başlık değiştirilmiş demektir. Tek başına kesin kanıt değildir, ama puanı etkileyen güçlü bir işarettir. Başlığın kendisini [User-Agent Nedir?](/tr/blog/what-is-user-agent) yazımızda anlattık.
- **IP'den bağımsız hız sınırı.** Yüzlerce IP'ye dağılmış bir trafik aynı parmak izini taşıyorsa, sayaç IP yerine parmak izine bağlanabilir. Dönen IP bu sayacı sıfırlamaz.
- **Bilinen araçları tanıma.** Zararlı yazılım aileleri ve tarama araçları için derlenmiş parmak izi listeleri vardır; güvenlik ekipleri bunları günlüklerle eşleştirir.

Sınırları da vardır. Güncel bir tarayıcının değerini engelleyen site, o tarayıcıyı kullanan bütün ziyaretçilerini engeller. Tarayıcı güncellemeleri değeri değiştirir, kurumsal ağ geçitleri ve antivirüsler kendi parmak izlerini araya koyar. Bu yüzden TLS parmak izi tek başına karar verdirmez; IP itibarı ve davranışla birlikte puana giren sinyallerden biridir. Bu puanlamanın bütününü [Bot Tespiti Nasıl Yapılır?](/tr/blog/how-bot-detection-works) yazımızda, Cloudflare'in katmanlarını ise [Cloudflare Precursor](/tr/blog/cloudflare-precursor) yazımızda ele aldık.

## Veri toplayan geliştirici için ne anlama geliyor?

Girişteki duruma dönelim. Python istemcisi `User-Agent` satırında Chrome olduğunu söylüyor, `ClientHello` ise OpenSSL olduğunu. Site bu çelişkiyi ilk pakette görüyor. Doğru tepki çelişkiyi gizlemeye çalışmak değil, ortadan kaldırmaktır:

- **Olmadığınız bir tarayıcı olduğunuzu iddia etmeyin.** Betiğinizin `User-Agent` değeri betiği tanıtsın: bir ad, bir sürüm ve iletişim adresi. Dürüst kimlikle gelen, `robots.txt` dosyasına uyan ve yavaş çalışan bir istemciyi site sahibi tanıyıp izin listesine alabilir. Kuralları [robots.txt Dosyası Nedir, Nasıl Okunur?](/tr/blog/robots-txt) yazımızda anlattık.
- **Resmi API varsa onu kullanın.** API anahtarıyla gelen istekte kimliğiniz zaten bellidir; parmak izi tartışması ortadan kalkar.
- **Sayfa gerçekten tarayıcı istiyorsa gerçek tarayıcı kullanın.** JavaScript ile çizilen bir sayfayı Playwright ile gerçek bir Chromium içinde açtığınızda istemciniz gerçekten o tarayıcıdır; başlıkları, TLS katmanı ve JavaScript ortamı birbiriyle tutarlıdır. Kurulumu [Playwright Nedir ve Proxy ile Nasıl Kullanılır?](/tr/blog/playwright-proxy) yazımızda.
- **Hızı düşürün ve izin isteyin.** Düzenli, büyük hacimli bir iş için site sahibine yazmak çoğu zaman en kalıcı çözümdür.

Engellenmenin diğer nedenlerini ve meşru çözümlerini [Web Scraping'de Engellenmeden Veri Toplama Yöntemleri](/tr/blog/web-scraping-without-getting-blocked) yazımızda topladık.

## Kullanım alanları

- **Scraper'da `403` teşhisi:** aynı IP ile tarayıcı geçiyor, betik geçemiyorsa fark büyük olasılıkla istemci kimliğindedir. Durum kodlarının ayrımı [Scraping'de HTTP Hata Kodları](/tr/blog/http-status-codes-web-scraping) yazımızda, genel kurgu [veri kazıma çözümü](/tr/data-scraping) sayfamızda.
- **Kendi sitenizde bot trafiğini ayırmak:** günlüklerde parmak izine göre gruplama, IP'ye göre gruplamanın kaçırdığı dağıtık trafiği gösterir.
- **Kendi tarayıcınızı (crawler) tanıtmak:** sabit bir kütüphane sürümü tutarlı bir parmak izi üretir, crawler'ınız günlüklerde böyle tanınır. Altyapı tarafı [web crawler çözümü](/tr/web-crawler) sayfamızda.
- **Proxy beklentisini doğru kurmak:** [Residential Proxy](https://proxynet.io/tr/residential-proxy) IP itibarını ve konumu değiştirir; istemcinizin kimliği sizin sorumluluğunuzda kalır.

## Sık yapılan hatalar

- **`User-Agent` değiştirince istemcinin değiştiğini sanmak.** Başlık bir metindir; `ClientHello` kütüphanenin davranışıdır ve başlıktan önce gider.
- **Proxy'den TLS parmak izini değiştirmesini beklemek.** Tünel proxy'si IP'yi değiştirir, el sıkışmaya dokunmaz.
- **Chromium tabanlı tarayıcıda JA3 özetinin sabit kalmasını beklemek.** Uzantı sırası her bağlantıda değişir; karşılaştırma için JA4 ya da sıralanmış uzantı listesi kullanın.
- **Yankı servisindeki sonucu sorgulamadan kabul etmek.** HTTPS taraması yapan bir antivirüs ya da kurumsal ağ geçidi, servise kendi parmak izini gösterir.
- **Ölçümde `127.0.0.1` kullanmak.** SNI gönderilmez, gerçek bağlantılardakinden farklı bir değer çıkar.

## Karar rehberi

| Durum | Öneri |
|---|---|
| Proxy ile IP değişiyor, ilk istekte `403` alıyorsunuz | İstemci kimliğine bakın: `User-Agent` ile kullandığınız kütüphane aynı şeyi söylüyor mu? |
| Kendi istemcinizin JA3 ya da JA4 değerini öğrenmek istiyorsunuz | Yankı servisi ya da yukarıdaki yerel dinleyici |
| Chrome trafiğini günlüklerde gruplamak istiyorsunuz | JA3 değil JA4; uzantı karıştırmadan etkilenmez |
| Sayfa JavaScript ve gerçek tarayıcı istiyor | Gerçek bir tarayıcı otomasyonu (Playwright), makul hız, sitenin izin verdiği kapsam |
| Yankı servisi beklemediğiniz bir değer gösteriyor | Sertifikayı kimin imzaladığına bakın; arada TLS sonlandıran bir yazılım olabilir |
| Sitenizde dağıtık bot trafiği var | Hız sınırını IP yerine IP + parmak izi çiftine bağlayın, tek özete göre engellemeyin |

## Sıkça sorulan sorular

### JA3 ile JA3S arasındaki fark nedir?

JA3 istemcinin `ClientHello` mesajından, JA3S sunucunun `ServerHello` mesajından üretilir. Sunucu farklı istemcilere farklı yanıt verdiği için JA3S tek başına sunucuyu tanımlamaz; aynı istemciye verilen yanıt ise hep aynıdır. Güvenlik ekipleri bu yüzden ikisini çift olarak kullanır.

### VPN kullanmak TLS parmak izini değiştirir mi?

Hayır. VPN trafiği şifreli bir tünelden geçirir, ama site ile TLS el sıkışmasını yine sizin tarayıcınız ya da betiğiniz yapar. Site VPN sunucusunun IP adresini ve sizin istemcinizin parmak izini görür. İki aracın farkını [Proxy ve VPN Farkı](/tr/blog/proxy-vs-vpn) yazımızda anlattık.

### TLS parmak izi beni kişisel olarak tanımlar mı?

Tek başına hayır. Aynı işletim sisteminde aynı tarayıcı sürümünü kullanan herkes aynı değeri üretir. Kişiye yaklaşan kimlik, bu değerin IP adresi, çerezler ve JavaScript katmanındaki sinyallerle birleştirilmesinden doğar.

### Tarayıcıyı güncellemek parmak izini değiştirir mi?

Değiştirebilir. Yeni sürüm bir şifre takımını kaldırdığında ya da yeni bir uzantı eklediğinde liste ve özet değişir. Parmak izi listeleri bu yüzden sürüm sürüm tutulur.

### Gizli pencere ya da çerezleri silmek TLS parmak izini etkiler mi?

Hayır. `ClientHello` mesajını tarayıcının TLS kütüphanesi üretir; geçmiş, çerezler ve pencere türü bu listelere girmez.

### Parmak izim yüzünden engelleniyorsam ne yapmalıyım?

Önce ölçün: yankı servisinde hangi değerin göründüğüne ve arada TLS sonlandıran bir yazılım olup olmadığına bakın. Betik yazıyorsanız kimliğinizi dürüstçe belirtin, hızınızı düşürün ve sitenin resmi API'sini ya da izin kanalını kullanın. Sıradan bir tarayıcıyla engelleniyorsanız sorun büyük olasılıkla parmak izinde değil IP itibarındadır.

## Özet

TLS parmak izi, şifreli bağlantının ilk ve şifresiz paketi olan `ClientHello` mesajından çıkar. JA3 bu mesajdaki beş listeyi sırasıyla birleştirip MD5 özetini alır; Chrome uzantı sırasını karıştırmaya başlayınca özet kararsızlaştı, JA4 listeleri sıralayarak bunu giderdi. Değer kişiyi değil yazılımı gösterir, başlıklardan önce gider ve tünel kuran hiçbir proxy ona dokunmaz: proxy IP'yi değiştirir, istemcinin kimliği sizde kalır. Tutarlı ve dürüst bir istemci kimliğinin yanına doğru IP türünü eklemek için [proxy hizmetlerimize](/tr/proxy) göz atabilirsiniz.
