---
title: 'HTTP İstek ve Yanıtı'
source: 'https://academia.sh/tr/kurslar/internet-nasil-calisir/http-istek-ve-yaniti'
course: 'İnternet Nasıl Çalışır'
language: tr
updated: '2026-08-17T18:07:05+00:00'
license: 'CC BY-SA 4.0'
---

# HTTP İstek ve Yanıtı

İsteğin ve yanıtın parçaları, yöntemlerin güvenlik ve etkisizlik özellikleri, durum kodu sınıfları ve protokolün durumsuzluğu.

Önceki konu, adres satırına yazılan adın nasıl bir sunucuya vardığını kurdu. İstemci–Sunucu
Modeli dersinde bu sunucuya bağlanılmış ve alışverişin iki ucu da görülmüştü. Ancak
gönderilen metnin neden o biçimde olduğu açıklanmadı.

Bu ders o biçimi tanımlar. **HTTP**, istemci ile sunucu arasındaki alışverişin kurallarını
belirleyen protokoldür ve web'in üzerine kurulduğu sözleşmedir.

## Metin Tabanlı Bir Protokol

İstemci–Sunucu Modeli dersindeki sunucu, aldığı baytları olduğu gibi ekrana yazmıştı:

```
GET / HTTP/1.1
Host: ornek.test:8080
User-Agent: ornek-istemci
Accept: */*

```

Bu, ikilik bir yapı değil, okunabilir bir metindir. Tasarımın bu yönde olması bir seçimdir
ve bedeli vardır: metin gösterimi, aynı bilgiyi ikilik gösterimden daha çok baytla taşır ve
ayrıştırılması daha pahalıdır. Karşılığında protokol, özel bir araç gerekmeden incelenebilir
ve genişletilebilir olur; yeni bir başlık eklemek var olan gerçekleştirimleri bozmaz.

Satır sonları `\r\n` iki karakteriyle yazılır. Başlıkların bittiğini gösteren şey, boş bir
satırdır — yukarıdaki çıktının sonundaki boşluk budur.

## İsteğin Yapısı

Bir istek üç bölümden oluşur.

**İstek satırı**, ilk satırdır ve üç alan taşır: yöntem, hedef, sürüm.

```
GET / HTTP/1.1
```

`GET` yapılacak işlemi, `/` istenen kaynağın yolunu, `HTTP/1.1` istemcinin konuştuğu
protokol sürümünü bildirir. Hedefin yalnızca yol bölümü yazılır; alan adı ayrı bir
başlıkta taşınır.

**Başlıklar**, her satırda bir `ad: değer` çifti taşır. Başlık adları büyük–küçük harf
ayrımı gözetmez. Yukarıdaki üç başlığın işlevleri farklıdır: `Host` hangi siteye
bağlanıldığını bildirir ve Barındırma Modelleri dersinde görüldüğü gibi sanal barındırmanın
dayanağıdır; `User-Agent` istemciyi tanıtır; `Accept` istemcinin hangi içerik biçimlerini
işleyebildiğini söyler.

**Gövde**, boş satırdan sonra gelen isteğe bağlı veridir. `GET` isteklerinde genellikle
bulunmaz; veri gönderen yöntemlerde bulunur.

## Yanıtın Yapısı

Yanıt aynı düzeni izler, yalnızca ilk satırı farklıdır.

```
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 73
Connection: close

<!doctype html>
<html lang="tr"><body><h1>Ornek Sayfa</h1></body></html>
```

**Durum satırı** sürümü, sayısal durum kodunu ve okunabilir açıklamasını taşır. Açıklama
metni bilgilendirme amaçlıdır; kararlar sayıya göre verilir.

`Content-Length` başlığı gövdenin kaç bayt olduğunu bildirir. Bu başlık bir ayrıntı değil,
protokolün işlemesi için zorunlu bir bilgidir: alıcının gövdenin nerede bittiğini bilmesi
gerekir. Bağlantı bir bayt akışıdır ve akışta doğal bir sınır yoktur. Uzunluk önceden
bilinmiyorsa gövde parçalar hâlinde, her parçanın başında boyu yazılarak gönderilir.

`Content-Type` gövdenin nasıl yorumlanacağını söyler. Buradaki `charset=utf-8` bölümü,
Bilgisayarlar Nasıl Çalışır kursundaki karakter kodlamaları dersinde kurulan soruyu
yanıtlar: baytların hangi kurala göre karakterlere çevrileceği alıcının tahminine
bırakılmaz, açıkça bildirilir.

## Yöntemler

Yöntem, kaynak üzerinde yapılacak işlemi bildirir. Yöntemleri ayıran iki özellik vardır ve
bu özellikler istemcilerin davranışını belirler.

**Güvenli (safe)** bir yöntem sunucuda değişiklik yapmaz; yalnızca okur. **Etkisiz
(idempotent)** bir yöntem, bir kez ya da birden çok kez uygulandığında aynı sonucu bırakır.
Her güvenli yöntem etkisizdir; tersi doğru değildir.

| Yöntem | İşlevi | Güvenli | Etkisiz |
|---|---|---|---|
| `GET` | Kaynağı getirir | Evet | Evet |
| `HEAD` | Yalnızca başlıkları getirir | Evet | Evet |
| `POST` | Veri gönderir, yeni kaynak oluşturabilir | Hayır | Hayır |
| `PUT` | Kaynağı verilen içerikle değiştirir | Hayır | Evet |
| `DELETE` | Kaynağı siler | Hayır | Evet |

Bu özellikler kuramsal değildir. Bir yanıt alınamadığında isteğin yinelenip
yinelenemeyeceği doğrudan buradan çıkar: etkisiz bir istek güvenle tekrarlanabilir,
`POST` isteği tekrarlanırsa aynı işlem iki kez yapılabilir. `DELETE` yöntemi ikinci
çağrıda farklı bir durum kodu döndürse de sunucunun durumu aynı kalır; etkisizlik, dönen
kodun değil bırakılan durumun özelliğidir.

Bu özelliklerin sunucu tarafından **korunması gerekir**; protokol bunu zorlayamaz. Durumu
değiştiren bir `GET` işleyicisi yazmak sözdizimsel olarak olanaklıdır ve protokole
aykırıdır.

`HEAD` yöntemi, `GET` ile aynı başlıkları döndürür ancak gövde göndermez. Bir kaynağın
boyutunu veya varlığını içeriği indirmeden öğrenmek için kullanılır.

## Durum Kodları

Durum kodu üç basamaklıdır ve ilk basamağı sonucun sınıfını verir.

| Sınıf | Anlamı |
|---|---|
| `1xx` | Bilgilendirme; işlem sürüyor |
| `2xx` | Başarı |
| `3xx` | Yönlendirme; kaynak başka yerde |
| `4xx` | İstemci hatası; istek kusurlu |
| `5xx` | Sunucu hatası; istek geçerliydi ama sunucu yerine getiremedi |

`4xx` ile `5xx` arasındaki ayrım işletme açısından belirleyicidir: birincisi isteği
düzeltmeyi, ikincisi sunucuyu incelemeyi gerektirir.

Aşağıdaki sunucu üç farklı durum kodu üretir ve bir yönlendirme tanımlar:

```python
from http.server import BaseHTTPRequestHandler, HTTPServer


class Isleyici(BaseHTTPRequestHandler):
    protocol_version = "HTTP/1.1"

    def log_message(self, bicem: str, *arg: object) -> None:
        pass

    def _gonder(self, durum: int, govde: bytes, ek: list[tuple[str, str]] = []) -> None:
        self.send_response_only(durum)
        self.send_header("Content-Type", "text/plain; charset=utf-8")
        self.send_header("Content-Length", str(len(govde)))
        for ad, deger in ek:
            self.send_header(ad, deger)
        self.end_headers()
        self.wfile.write(govde)

    def do_GET(self) -> None:
        if self.path == "/eski":
            self._gonder(301, b"", [("Location", "/yeni")])
        elif self.path == "/yeni":
            self._gonder(200, b"yeni adresteki icerik\n")
        else:
            self._gonder(404, b"bulunamadi\n")

    def do_POST(self) -> None:
        uzunluk = int(self.headers.get("Content-Length", "0"))
        govde = self.rfile.read(uzunluk)
        self._gonder(201, b"alinan govde: " + govde + b"\n")


HTTPServer(("127.0.0.1", 8080), Isleyici).serve_forever()
```

Üç yol sorgulandığında dönen kodlar:

```sh
for yol in /yeni /eski /yok; do
  printf "%-6s " "$yol"
  curl -s -o /dev/null -w "%{http_code}\n" --resolve ornek.test:8080:127.0.0.1 "http://ornek.test:8080$yol"
done
```

```
/yeni  200
/eski  301
/yok   404
```

Yönlendirme yanıtının kendisi incelendiğinde, kaynağın yeni yerinin bir başlıkta
taşındığı görülür:

```sh
curl -sD - -o /dev/null --resolve ornek.test:8080:127.0.0.1 http://ornek.test:8080/eski
```

```
HTTP/1.1 301 Moved Permanently
Content-Type: text/plain; charset=utf-8
Content-Length: 0
Location: /yeni
```

Yanıtın gövdesi boştur; taşınan bilgi `Location` başlığındadır. İstemci bu yanıtı alınca
yeni hedefe ikinci bir istek yapar. `curl` bunu kendiliğinden yapmaz; istendiğinde yapar:

```sh
curl -sL --resolve ornek.test:8080:127.0.0.1 http://ornek.test:8080/eski
```

```
yeni adresteki icerik
```

Tarayıcılar yönlendirmeleri kendiliğinden izler. `301` kodu taşınmanın kalıcı olduğunu
bildirir ve istemcinin eski adresi anımsamamasına izin verir; geçici taşınmalar için ayrı
kodlar vardır ve bu ayrım önemlidir, çünkü kalıcı yönlendirmeler istemcide saklanabilir.

Gövde taşıyan bir istek de aynı düzeni izler:

```sh
curl -s -X POST -d "ad=deneme" --resolve ornek.test:8080:127.0.0.1 http://ornek.test:8080/kayit
```

```
alinan govde: ad=deneme
```

## Durumsuzluk

HTTP **durumsuz (stateless)** bir protokoldür: sunucu, iki istek arasında istemciye ilişkin
hiçbir şey anımsamaz. Her istek kendi başına, önceki isteklerden bağımsız olarak yorumlanır.

Bu, gözlenebilir bir özelliktir. Aşağıdaki sunucu, isteğin taşıdığı sayacı okur ve bir
artırılmış değeri istemciye geri verir:

```python
from http.server import BaseHTTPRequestHandler, HTTPServer


class Isleyici(BaseHTTPRequestHandler):
    protocol_version = "HTTP/1.1"

    def log_message(self, bicem: str, *arg: object) -> None:
        pass

    def do_GET(self) -> None:
        gelen = self.headers.get("Cookie", "")
        sayac = int(gelen.removeprefix("sayac=")) if gelen.startswith("sayac=") else 0
        govde = f"gorulen sayac: {sayac}\n".encode()
        self.send_response_only(200)
        self.send_header("Content-Type", "text/plain; charset=utf-8")
        self.send_header("Content-Length", str(len(govde)))
        self.send_header("Set-Cookie", f"sayac={sayac + 1}")
        self.end_headers()
        self.wfile.write(govde)


HTTPServer(("127.0.0.1", 8080), Isleyici).serve_forever()
```

Üç istek, aralarında hiçbir şey taşımadan yapılırsa:

```sh
for i in 1 2 3; do curl -s --resolve ornek.test:8080:127.0.0.1 http://ornek.test:8080/; done
```

```
gorulen sayac: 0
gorulen sayac: 0
gorulen sayac: 0
```

Sunucu üç kez de sıfır gördü. Kendisinde hiçbir kayıt tutmuyor.

Aynı üç istek, sunucunun verdiği değeri geri taşıyarak yapılırsa:

```sh
rm -f kavanoz.txt
for i in 1 2 3; do curl -s -b kavanoz.txt -c kavanoz.txt --resolve ornek.test:8080:127.0.0.1 http://ornek.test:8080/; done
```

```
gorulen sayac: 0
gorulen sayac: 1
gorulen sayac: 2
```

Sunucu kodu değişmedi. Değişen tek şey, istemcinin `Set-Cookie` ile verilen değeri
saklayıp bir sonraki istekte `Cookie` başlığıyla geri göndermesidir. Durum sunucuda değil,
**istekte** taşınmaktadır.

Durumsuzluğun gerekçesi ölçeklenmedir. Sunucu istemciye özel bir bellek tutmadığı için,
ardışık iki istek farklı makineler tarafından yanıtlanabilir; sunucu çökerse
yeniden başlatıldığında kaybolan bir oturum durumu olmaz. Bedeli, her isteğin kendini
tanıtmak için gereken bilgiyi yeniden taşımasıdır.

## Özet

- HTTP metin tabanlı bir protokoldür; ayrıştırma maliyeti karşılığında incelenebilirlik ve
  genişletilebilirlik sağlar.
- İstek, istek satırı, başlıklar, boş satır ve isteğe bağlı gövdeden oluşur; yanıt aynı
  düzeni durum satırıyla izler.
- `Content-Length`, bayt akışında gövdenin nerede bittiğini bildirdiği için zorunludur;
  `Content-Type` baytların nasıl yorumlanacağını söyler.
- Yöntemler güvenli ve etkisiz olma özellikleriyle ayrılır; bu özellikler bir isteğin
  yinelenip yinelenemeyeceğini belirler ve sunucu tarafından korunmalıdır.
- Durum kodunun ilk basamağı sınıfı verir; `4xx` isteğin, `5xx` sunucunun düzeltilmesini
  gerektirir.
- Protokol durumsuzdur; oturum sürekliliği sunucunun belleğiyle değil, istemcinin her
  istekte geri taşıdığı verilerle kurulur.

## Sonraki Adım

Bu ders tek bir isteği ve tek bir yanıtı inceledi. Oysa bir sayfa açıldığında yalnızca bir
istek yapılmaz: gelen belge başka kaynaklara başvurur ve her biri ayrıca istenir. Sonraki
ders, adres satırına yazılan metinden ekrandaki çizime uzanan aşamaları ele alacak ve bu
ek isteklerin nasıl ortaya çıktığını gerçek bir sunucunun kayıtları üzerinden gösterecek.
