HTTP durum kodları: tam referans ve pratikte en çok karıştırılanlar
Bütün HTTP durum kodlarının Türkçe açıklamalı listesi — ve asıl mesele: 301 mi 308 mi, 401 mi 403 mü, 422 mi 400 mü, 502 mi 503 mü? Next.js'te hangi kodun ne zaman döndüğü de dahil.
Durum kodlarının listesini ezberlemek kolay; doğru olanı seçmek zor. Bir API'de "kaydı bulamadım" ile "bu kaydı görmeye yetkin yok" arasındaki fark, bir yönlendirmede POST'un GET'e dönüp dönmemesi, bir hata sayfasında Google'ın adresi indeksten silip silmemesi — hepsi bu üç haneli sayının seçimine bağlı.
Bu yazı iki bölüm: önce tam liste (referans olarak kullan), sonra pratikte en çok yanlış seçilen kodlar ve Next.js'te bunların nasıl karşımıza çıktığı.
Beş sınıf
| Sınıf | Anlamı | Kısaca |
|---|---|---|
| 1xx | Bilgi | İstek alındı, işlem sürüyor |
| 2xx | Başarı | İstek başarıyla işlendi |
| 3xx | Yönlendirme | Tamamlamak için ek adım gerekiyor |
| 4xx | İstemci hatası | Hata isteği gönderende |
| 5xx | Sunucu hatası | Hata sunucuda |
Ayrım basit görünüyor ama sınırda kalan durumlar var: istemci geçerli bir istek gönderdi, sunucu da doğru çalışıyor, ama iş kuralı izin vermiyor — bu 4xx mi 5xx mi? (Cevap: 4xx. Aşağıda.)
1xx — Bilgi
| Kod | Ad | Anlamı |
|---|---|---|
| 100 | Continue | İstemci gövdeyi göndermeye devam edebilir |
| 101 | Switching Protocols | Sunucu Upgrade başlığındaki protokole geçiyor (WebSocket el sıkışması) |
| 102 | Processing | İstek alındı, henüz sonuç yok (WebDAV) |
| 103 | Early Hints | Yanıt hazır olmadan Link başlığıyla kaynak ön yükleme ipucu |
103 Early Hints pratikte işe yarayan tek modern 1xx: sunucu asıl yanıtı
hazırlarken tarayıcıya "şu CSS ve fontu şimdiden çekmeye başla" diyebiliyor.
2xx — Başarı
| Kod | Ad | Anlamı |
|---|---|---|
| 200 | OK | İstek başarılı; anlamı HTTP metoduna göre değişir |
| 201 | Created | Yeni kaynak oluşturuldu (genelde POST/PUT) |
| 202 | Accepted | Alındı ama henüz işlenmedi — asenkron işler için |
| 203 | Non-Authoritative Information | Üstveri kaynak sunucudan gelmiyor (nadiren; 200 tercih edilir) |
| 204 | No Content | Başarılı, gövde yok — başlıklar anlamlı olabilir |
| 205 | Reset Content | İstemci belgeyi sıfırlasın |
| 206 | Partial Content | Range isteğine kısmi yanıt (video akışı, indirme devam ettirme) |
| 207 | Multi-Status | Birden çok kaynak için ayrı ayrı durum (WebDAV) |
| 208 | Already Reported | Aynı üyeleri tekrar saymamak için (WebDAV) |
| 226 | IM Used | Delta kodlamayla dönen yanıt |
Çoğu API her şeye 200 döndürüyor. Oysa kayıt oluşturan bir uç nokta
201 Created + Location başlığı, silen bir uç nokta 204 No Content
döndürdüğünde istemci hiçbir gövdeyi ayrıştırmadan ne olduğunu anlıyor.
3xx — Yönlendirme
| Kod | Ad | Metot korunur mu? | Kalıcı mı? |
|---|---|---|---|
| 300 | Multiple Choices | — | — |
| 301 | Moved Permanently | Hayır (tarayıcılar POST'u GET'e çevirir) | Kalıcı |
| 302 | Found | Hayır (POST → GET) | Geçici |
| 303 | See Other | Hayır — her zaman GET'e çevirir | Geçici |
| 304 | Not Modified | — (önbellek yanıtı) | — |
| 305 | Use Proxy | ⚠️ Kullanımdan kaldırıldı | — |
| 306 | (kullanılmıyor) | ⚠️ Ayrılmış | — |
| 307 | Temporary Redirect | Evet | Geçici |
| 308 | Permanent Redirect | Evet | Kalıcı |
Bu tablonun tamamı tek bir ayrımdan ibaret: yönlendirme sonrası metot korunuyor mu?
- 301 / 302 eski kodlar. Tarayıcılar bunlarda POST'u GET'e çeviriyor — standart öyle demese de fiilî durum bu.
- 307 / 308 bu belirsizliği ortadan kaldırmak için geldi: metot ne ise o kalıyor. POST'a yönlendiriyorsan POST olarak gidiyor.
- 303 ise tam tersini kasten yapıyor: neyle geldiğine bakmadan GET'e çeviriyor.
Form gönderiminden sonra kullanıcıyı bir sonuç sayfasına göndermek istiyorsun.
307 kullanırsan sonuç sayfasına da POST ile gider; kullanıcı sayfayı
yenilediğinde tarayıcı "formu yeniden göndermek istiyor musun?" diye sorar ve
kayıt iki kez oluşabilir. 303, isteği GET'e çevirerek bu tuzağı kapatır.
Buna POST/Redirect/GET deniyor ve ödeme dönüşü gibi akışlarda kritik.
Kalıcı taşımada 301/308 kullan; arama motorları eski adresi indeksten
düşürüp sinyalleri yenisine taşır. 302/307 kullanırsan eski adres
indekste kalmaya devam eder. Yanlış seçim, site göçlerinde trafik kaybının
en yaygın sebeplerinden biri.
4xx — İstemci hatası
| Kod | Ad | Anlamı |
|---|---|---|
| 400 | Bad Request | İstek bozuk: sözdizimi geçersiz, gövde ayrıştırılamıyor |
| 401 | Unauthorized | Aslında "kimliği doğrulanmamış" — giriş gerekiyor |
| 402 | Payment Required | Ödeme gerekli; standart bir kullanımı yok |
| 403 | Forbidden | Kim olduğun biliniyor ama yetkin yok |
| 404 | Not Found | Kaynak yok — ya da varlığını gizlemek istiyorsun |
| 405 | Method Not Allowed | Metot tanınıyor ama bu kaynakta desteklenmiyor |
| 406 | Not Acceptable | İçerik pazarlığı sonucu uygun bir biçim bulunamadı |
| 407 | Proxy Authentication Required | 401'in vekil sunucu hâli |
| 408 | Request Timeout | Boşta bekleyen bağlantı kapatılıyor |
| 409 | Conflict | İstek, kaynağın mevcut durumuyla çelişiyor |
| 410 | Gone | Kalıcı olarak silindi, adresi de yok |
| 411 | Length Required | Content-Length başlığı zorunlu |
| 412 | Precondition Failed | Koşullu isteğin ön koşulu sağlanmadı |
| 413 | Content Too Large | Gövde sunucu sınırından büyük |
| 414 | URI Too Long | Adres çok uzun |
| 415 | Unsupported Media Type | Gönderilen biçim desteklenmiyor |
| 416 | Range Not Satisfiable | İstenen aralık kaynağın dışında |
| 417 | Expectation Failed | Expect başlığındaki beklenti karşılanamıyor |
| 418 | I'm a teapot | Şaka kodu (RFC 2324) — demlikten kahve çıkmaz |
| 421 | Misdirected Request | İstek yanlış sunucuya düştü |
| 422 | Unprocessable Content | Biçimi doğru ama anlamı geçersiz |
| 423 | Locked | Kaynak kilitli (WebDAV) |
| 424 | Failed Dependency | Önceki istek başarısız olduğu için (WebDAV) |
| 425 | Too Early | Tekrar oynatılabilecek isteği işlemeyi reddediyor (TLS 1.3 early data) |
| 426 | Upgrade Required | Protokolü yükseltmen gerekiyor |
| 428 | Precondition Required | Koşullu istek zorunlu — "kayıp güncelleme" sorununu önler |
| 429 | Too Many Requests | Hız sınırı aşıldı |
| 431 | Request Header Fields Too Large | Başlıklar çok büyük |
| 451 | Unavailable For Legal Reasons | Hukuki sebeple erişime kapalı |
401 mi 403 mü?
En çok karıştırılan çift. Adları yanıltıcı, çünkü 401 aslında "kimliği doğrulanmamış" demek:
- 401 — Sen kimsin bilmiyorum. Giriş yap. (Oturum yok ya da token geçersiz.)
- 403 — Kim olduğunu biliyorum, ama bu kaynağa erişemezsin. (
userrolündeki biri/admin'e girmeye çalışıyor.)
Bazen 403 bile fazla bilgi verir. "Bu kaynak var ama sen göremezsin" demek,
kaynağın varlığını sızdırır. Özel depoları listelerken GitHub'ın yaptığı gibi
404 döndürmek, varlığı tamamen gizlemenin meşru yolu.
400 mü 422 mi?
- 400 — İsteği ayrıştıramadım. Bozuk JSON, eksik zorunlu alan, yanlış tip.
- 422 — Ayrıştırdım, biçim doğru; ama anlamı geçersiz. "Bitiş tarihi başlangıçtan önce olamaz", "bu e-posta zaten kayıtlı".
Yani sözdizimi hatası 400, iş kuralı ihlali 422. Pratikte pek çok API ikisine de 400 döndürüyor ve bu da kabul edilebilir — ama ayırırsan istemci tarafında "formu düzelt" ile "isteği düzelt" ayrımını kod yazmadan yapabiliyorsun.
409 ve 410
- 409 Conflict — İstek kaynağın şu anki durumuyla çelişiyor. Eşzamanlı düzenlemede sürüm çakışması, aynı slug'ın ikinci kez oluşturulmaya çalışılması.
- 410 Gone — Kaynak vardı, kalıcı olarak silindi ve geri gelmeyecek.
404'ten farkı kesinlik: arama motorları410gördüğünde adresi daha hızlı düşürüyor. Emin değilsen404kullan.
429 ve Retry-After
Hız sınırlaması uyguluyorsan yalnız 429 döndürmek yarım iş. Retry-After
başlığını da gönder — istemci ne kadar bekleyeceğini bilsin, körlemesine tekrar
denemesin.
export async function POST(req: Request) {
const allowed = await rateLimit(req);
if (!allowed) {
return new Response(
JSON.stringify({ error: "Çok fazla istek gönderdiniz." }),
{
status: 429,
headers: {
"Content-Type": "application/json",
"Retry-After": "60", // saniye
},
}
);
}
// ...
}451
İçerik hukuki bir sebeple engellendiğinde. Ray Bradbury'nin Fahrenheit 451 romanına gönderme — kitapların yakıldığı sıcaklık. Türkiye'de erişim engelleri için de doğru koddur, ama uygulamada çoğu yerde kullanılmıyor.
5xx — Sunucu hatası
| Kod | Ad | Anlamı |
|---|---|---|
| 500 | Internal Server Error | Genel hata; başka bir şey söylenemiyor |
| 501 | Not Implemented | Sunucu bu metodu desteklemiyor |
| 502 | Bad Gateway | Ara sunucu, arkadaki sunucudan geçersiz yanıt aldı |
| 503 | Service Unavailable | Sunucu geçici olarak hizmet veremiyor (bakım, aşırı yük) |
| 504 | Gateway Timeout | Arkadaki sunucu zamanında cevap vermedi |
| 505 | HTTP Version Not Supported | HTTP sürümü desteklenmiyor |
| 506 | Variant Also Negotiates | Sunucu yapılandırma hatası (döngüsel pazarlık) |
| 507 | Insufficient Storage | Depolama yetersiz (WebDAV) |
| 508 | Loop Detected | Sonsuz döngü algılandı (WebDAV) |
| 510 | Not Extended | İstenen HTTP uzantısı desteklenmiyor |
| 511 | Network Authentication Required | Ağa erişim için kimlik doğrulama (otel/kafe wifi portalı) |
502, 503, 504 — hangisi hangi arıza?
Bu üçü sunucu kurulumunda teşhis koymanın en hızlı yolu:
- 502 Bad Gateway — Nginx ayakta, ama arkadaki uygulama çökmüş ya da o porta cevap vermiyor. Uygulama loglarına bak.
- 503 Service Unavailable — Sunucu ayakta ama şu an hizmet vermek
istemiyor: bakım modu, aşırı yük, havuzda örnek yok.
Retry-Afterile birlikte gönderilmeli ve önbelleğe alınmamalı. - 504 Gateway Timeout — Arkadaki uygulama var ama çok yavaş; ara sunucu beklemekten vazgeçti. Yavaş sorgu ya da dış API çağrısı ara.
"Bakımdayız" sayfasını 200 ile sunmak, arama motorlarının o sayfayı sitenin
gerçek içeriği sanmasına yol açar. Bakım sayfası 503 döndürmeli — kısa
süreli bir kesintiden sonra sıralamana bir şey olmaz.
Next.js'te durum kodları
Framework bazı kodları senin yerine seçiyor ve hangisini seçtiğini bilmek işe yarıyor.
redirect()→ 307 (geçici, metot korunur)permanentRedirect()→ 308 (kalıcı, metot korunur)- Server Action form gönderiminde
redirect()→ 303 (tarayıcı GET ile takip etsin diye; JavaScript varsa zaten istemci tarafı gezinme yapılıyor) NextResponse.redirect()→ varsayılan 307
Next.js'in 302/301 yerine 307/308 seçmesinin sebebi yukarıdaki metot
sorunu: 302 alan tarayıcı POST'u GET'e çeviriyor ve /users'a yaptığın
POST, /people'a GET olarak varıyor.
Kalıcı bir adres değişikliği yapıyorsan redirect() değil
permanentRedirect() kullan — ya da next.config.ts içindeki redirects()
tanımında permanent: true ver. Aksi hâlde arama motoru eski adresi düşürmez.
notFound() çağrısı 404 döndürür ve not-found.tsx dosyanı gösterir.
import { NextResponse } from "next/server";
export async function POST(req: Request) {
const body = await req.json().catch(() => null);
if (!body) {
return NextResponse.json({ error: "Geçersiz JSON" }, { status: 400 });
}
const parsed = schema.safeParse(body);
if (!parsed.success) {
// Biçim doğru, anlam geçersiz
return NextResponse.json(
{ error: "Doğrulama hatası", issues: parsed.error.issues },
{ status: 422 }
);
}
const session = await auth();
if (!session) {
return NextResponse.json({ error: "Giriş gerekli" }, { status: 401 });
}
if (session.user.role !== "admin") {
return NextResponse.json({ error: "Yetkiniz yok" }, { status: 403 });
}
const created = await db.post.create({ data: parsed.data });
return NextResponse.json(created, {
status: 201,
headers: { Location: `/api/posts/${created.id}` },
});
}Hızlı karar rehberi
- Kayıt oluşturdum → 201 +
Location - Sildim, dönecek bir şey yok → 204
- Adres kalıcı değişti → 308 (SEO için
301de kabul) - Form sonrası sonuç sayfasına gönderiyorum → 303
- Giriş yapılmamış → 401
- Giriş yapılmış ama yetkisiz → 403
- Kaydın varlığını gizlemem gerekiyor → 404
- JSON bozuk → 400
- JSON doğru, iş kuralı ihlal → 422
- Eşzamanlı düzenleme çakışması → 409
- Hız sınırı → 429 +
Retry-After - Bakımdayım → 503 +
Retry-After - Beklenmedik hata → 500 (ve loglara gerçek sebebi yaz, kullanıcıya değil)
Son bir not: hata gövdesinde kullanıcıya yığın izi (stack trace) gösterme. Durum kodu istemciye ne yapacağını söylemek için; ayrıntı loglara ait.
Kod listesi MDN HTTP response status
codes
referansına, Next.js davranışları
redirect dokümantasyonuna
dayanıyor.
Bu yazıyı faydalı bulduysan paylaşabilirsin.