Bir API sürümünü emekliye ayırırken istemcilere iki ayrı bilgi verirsiniz: "bu kaynak kullanımdan kalktı veya kalkacak" ve "bu kaynak şu tarihte yanıt vermeyi bırakacak". HTTP'de ilki için RFC 9745 ile standartlaşan Deprecation başlığı (Deprecation: @1796083200), ikincisi için RFC 8594 tanımlı Sunset başlığı (Sunset: Tue, 01 Jun 2027 00:00:00 GMT) kullanılır. Ayrıntılı geçiş belgesine ise Link başlığında rel="deprecation" ve rel="sunset" ilişkileriyle bağlantı verilir.
Bu yazıda iki başlığın sözdizimini, birbirleriyle ilişkisini ve aralarındaki tarih biçimi farkını RFC metinlerine dayanarak açıklıyor, bir FastAPI ara katmanıyla (middleware) v1 yanıtlarına bu başlıkları ekleyip kapanış tarihinden sonra 410 döndüren çalışan bir örnek veriyoruz. Yeni sürümün nasıl açılacağı API versiyonlama yazısında, hangi değişikliğin yeni sürüm gerektirdiği breaking change yazısında anlatılıyor. Serinin tamamı için REST API tasarımı öğrenme rehberine bakın.
Kullanımdan kaldırma iki aşamadır

RFC 8594, Web API'lerinde kullanımdan kaldırmanın (deprecation) çoğu zaman iki aşamada gerçekleştiğini anlatır. İlk aşamada API veya belirli bir sürümü artık tercih edilen ya da önerilen sürüm değildir ama çalışmaya devam eder. İkinci aşamada o sürüm hizmetten çekilir (decommission) ve yanıt vermez hâle gelir. İki başlık bu iki aşamaya karşılık gelir:
| Özellik | Deprecation | Sunset |
|---|---|---|
| Tanımlayan belge | RFC 9745 (Mart 2025, standart) | RFC 8594 (Mayıs 2019) |
| Anlattığı | Kaynak kullanımdan kalktı veya kalkacak | Kaynak bu tarihte yanıt vermeyebilir |
| Değer biçimi | Structured Field tarihi: @ ve Unix zamanı | HTTP-date: Tue, 01 Jun 2027 00:00:00 GMT |
| Geçmiş tarih anlamı | Kaynak o tarihte kullanımdan kalktı | Kaynak her an kapanabilir |
| Kaynağın davranışı | Değişmez, kaynak çalışmaya devam eder | Tarihten sonra 4xx, 3xx veya hiç yanıt |
| Bağlantı ilişkisi | rel="deprecation": geçiş belgeleri | rel="sunset": emeklilik politikası |
RFC 8594 açıkça, ilk aşama için Sunset başlığının uygun olmadığını söyler; çünkü o aşamada API hâlâ çalışır ve kullanılabilir. Sunset yalnızca gerçekten yanıt vermeyi bırakma anını duyurmak içindir. İlk aşamanın standart sinyali, sonradan yayımlanan RFC 9745 ile gelen Deprecation başlığıdır.
Deprecation başlığı
RFC 9745'e göre Deprecation, yanıtın ait olduğu kaynağın kullanımdan kalkacağını veya kalktığını bildiren bir yanıt başlığıdır. Değeri, Structured Field Values (RFC 9651) biçiminde bir tarihtir: @ işaretinin ardından 1 Ocak 1970 UTC'den bu yana geçen saniye sayısı gelir. RFC'nin kendi örneği Deprecation: @1688169599, yani 30 Haziran 2023 23:59:59 UTC'dir. Tarih gelecekte olabilir (kaynak o tarihte kullanımdan kalkacak) ya da geçmişte (kaynak o tarihte kullanımdan kalktı).
RFC'nin üzerinde durduğu en önemli nokta şudur: kullanımdan kaldırma, kaynağın davranışını değiştirmez. Başlığın yanıtta görünmesi, kaynağın anlamının veya işlevinin değiştiği anlamına gelmez; istemciler kaynağı önceki gibi kullanabilir. Başlık bir ipucudur (hint) ve istemcilerin bu bilgi olmadan da çalışabilecek şekilde yazılması beklenir. Öte yandan RFC, tarih geçmişteyse istemci geliştiricilerinin kaynağın davranışının eskisi gibi kalacağını artık varsaymaması gerektiğini de ekler: işlevsel olmayan ayrıntılar, örneğin yanıt süreleri, etkilenebilir.
Sunset başlığı
RFC 8594'e göre Sunset, kaynağın belirli bir anda yanıt vermez hâle gelmesinin beklendiğini bildirir. Değeri tek bir HTTP-date zaman damgasıdır ve gelecekte olması gerekir. RFC 9110, HTTP-date üretirken Sun, 06 Nov 1994 08:49:37 GMT biçimindeki IMF-fixdate formatının kullanılmasını zorunlu tutar; saat her zaman UTC olarak yorumlanır ve GMT kısaltmasıyla yazılır. Geçmiş bir tarih gelirse, RFC 8594 bunu en güvenli biçimde "şimdi" olarak yorumlamayı, yani kaynağın her an kapanabileceğini düşünmeyi önerir.
Sunset da bir ipucudur: kaynağın o ana kadar mutlaka çalışacağını veya o andan sonra mutlaka kapanacağını garanti etmez. RFC, tarih geçtikten sonra etkileşimlerin büyük olasılıkla istemci hatası (4xx), yönlendirme (3xx) ya da hiç yanıt alınamaması ile sonuçlanacağını, başlığın bunlardan hangisinin olacağını söylemediğini belirtir. Başlığı tanımayan istemciler normal çalışmaya devam eder ve kaynak kapandığında bunu önceden bir bildirim görmeden yaşar. Bu yüzden başlıklar tek başına bir duyuru kanalı değildir; belgelerdeki duyuruyu tamamlarlar.
İki tarih arasındaki kural ve biçim farkı
RFC 9745, iki başlık birlikte kullanıldığında Sunset zaman damgasının Deprecation zaman damgasından önce olamayacağını kesin bir kural olarak koyar. Bir yapılandırma hatası nedeniyle bu sıra bozulursa, istemci geliştiricisinin kaynağın sahibine danışması beklenir. Mantığı açıktır: bir şey önce kullanımdan kalkar, sonra kapanır.
Aynı RFC, tarihsel nedenlerle iki başlığın farklı tarih biçimi kullandığını not eder. Bu, uygulamada en sık hata yapılan noktadır: Deprecation değerine HTTP-date ya da ISO 8601 metni yazmak veya Sunset değerine @ ile başlayan Unix zamanı yazmak, başlığı tanıyan istemcilerin onu ayrıştıramamasına yol açar. Tabloda gördüğünüz iki biçimi birbirine karıştırmayın; tarihleri kodda tek bir yerde tutup her başlık için doğru biçime çeviren küçük bir yardımcı yazmak bu hatayı önler.
Link ilişkileri: deprecation ve sunset

Her iki RFC de başlığa ek olarak RFC 8288 Web Linking çerçevesinde bir bağlantı ilişki türü kaydeder. rel="deprecation", insanların okuması için hazırlanmış, kullanımdan kaldırmayla ilgili belgeye işaret eder: geçiş rehberi, yeni kaynak, takvim. rel="sunset" ise kaynağın emeklilik politikası hakkında bilgi veren bir kaynağa işaret eder. Birlikte kullanılmış tam bir v1 yanıtı şöyle görünür:
HTTP/1.1 200 OK
Content-Type: application/json
Deprecation: @1796083200
Sunset: Tue, 01 Jun 2027 00:00:00 GMT
Link: <https://api.example.com/belgeler/v1-gecis>; rel="deprecation"; type="text/html"
{"id": 7, "name": "Ayşe Demir"}Bağlantılar başlıklardan önce de yayımlanabilir. RFC 9745'in örneğinde yanıtta Deprecation başlığı yokken rel="deprecation" bağlantısı vardır: kaynak henüz kullanımdan kalkmamıştır ama kullanımdan kaldırma politikasının nerede yazdığı şimdiden bulunabilir. RFC 8594 de benzer biçimde, Sunset başlığı henüz görünmeden önce emeklilik politikasının rel="sunset" bağlantısıyla duyurulabileceğini anlatır. Böyle bir politika bağlantısı, güncel v2 yanıtlarında bile bulunabilir:
HTTP/1.1 200 OK
Content-Type: application/json
Link: <https://api.example.com/belgeler/surum-politikasi>; rel="sunset"; type="text/html"
{"id": 7, "full_name": "Ayşe Demir"}Güvenlik açısından RFC 9745, istemcilerin bağlantı verilen belgenin gerçekliğini ve doğruluğunu kontrol etmesi gerektiğini ve HTTPS üzerinden sunulmayan Link içeriğinin güvenli sayılmaması gerektiğini vurgular. RFC 8594 ise makine tarafından okunabilir emeklilik bilgisine göre otomatik işlem yapmadan önce bilginin gerçek ve doğru olduğunun doğrulanmasını ister. Bağlantıları her zaman HTTPS adresleriyle verin.
Başlığın kapsamı
İki başlık da varsayılan olarak yalnızca yanıtın ait olduğu kaynak için geçerlidir. Bir sürümün tamamını emekliye ayırırken bu, v1 altındaki her yanıta başlıkları eklemek anlamına gelir. RFC'ler kapsamın genişletilmesine de izin verir: örneğin API yalnızca giriş belgesinde (home document) başlığı gösterip bunun tüm API için geçerli olduğunu belgeleyebilir. Ancak RFC'ler, bu genişletilmiş kapsamın kuralı bilmeyen istemciler için görünmez olduğunu ve onların başlığı yalnızca o kaynağa ait sayacağını hatırlatır. Pratikte en anlaşılır yol, bir sürümün bütün yanıtlarına başlıkları eklemektir; aşağıdaki örnek de bunu bir ara katmanla yapıyor.
FastAPI ile çalışan örnek
Örnekte v1 uç noktaları 1 Aralık 2026'da kullanımdan kalkacak ve 1 Haziran 2027'de kapanacak. Ara katman yalnızca /v1/ ile başlayan yollara dokunuyor; kapanış tarihinden önce yanıta üç başlığı ekliyor, sonra 410 döndürüyor. Saati test edebilmek için bir sözlükte tutuyoruz; gerçek uygulamada datetime.now(timezone.utc) kullanılır:
from datetime import datetime, timezone
from email.utils import format_datetime
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from fastapi.testclient import TestClient
KULLANIMDAN_KALKIS = datetime(2026, 12, 1, tzinfo=timezone.utc)
KAPANIS = datetime(2027, 6, 1, tzinfo=timezone.utc)
SIMDI = {"an": datetime(2026, 11, 7, tzinfo=timezone.utc)} # test için ayarlanabilir saat
app = FastAPI()
@app.middleware("http")
async def v1_uyarisi(request: Request, call_next):
if not request.url.path.startswith("/v1/"):
return await call_next(request)
if SIMDI["an"] >= KAPANIS:
return JSONResponse({"detail": "v1 kapatıldı, /v2 kullanın"}, status_code=410)
yanit = await call_next(request)
yanit.headers["Deprecation"] = f"@{int(KULLANIMDAN_KALKIS.timestamp())}"
yanit.headers["Sunset"] = format_datetime(KAPANIS, usegmt=True)
yanit.headers["Link"] = ('<https://api.example.com/belgeler/v1-gecis>; rel="deprecation"; '
'type="text/html", <https://api.example.com/belgeler/surum-politikasi>; '
'rel="sunset"; type="text/html"')
return yanit
@app.get("/v1/kullanicilar/{kid}")
def v1_getir(kid: int):
return {"id": kid, "name": "Ayşe Demir"}
@app.get("/v2/kullanicilar/{kid}")
def v2_getir(kid: int):
return {"id": kid, "full_name": "Ayşe Demir"}
c = TestClient(app)
r = c.get("/v1/kullanicilar/7")
print(r.status_code, r.json())
# çıktı: 200 {'id': 7, 'name': 'Ayşe Demir'}
for b in ("deprecation", "sunset", "link"):
print(f"{b}: {r.headers[b]}")
# çıktı: deprecation: @1796083200
# çıktı: sunset: Tue, 01 Jun 2027 00:00:00 GMT
# çıktı: link: <https://api.example.com/belgeler/v1-gecis>; rel="deprecation"; type="text/html", <https://api.example.com/belgeler/surum-politikasi>; rel="sunset"; type="text/html"
print("v2 Deprecation var mı:", "deprecation" in c.get("/v2/kullanicilar/7").headers)
# çıktı: v2 Deprecation var mı: False
SIMDI["an"] = datetime(2027, 6, 2, tzinfo=timezone.utc)
r = c.get("/v1/kullanicilar/7")
print(r.status_code, r.json())
# çıktı: 410 {'detail': 'v1 kapatıldı, /v2 kullanın'}
Kodda iki biçim dönüşümüne dikkat edin. Deprecation değeri timestamp() ile saniyeye çevrilip başına @ ekleniyor. Sunset değeri ise Python standart kütüphanesindeki email.utils.format_datetime işleviyle usegmt=True verilerek IMF-fixdate biçiminde üretiliyor. Çıktıda v2 yanıtında başlık olmadığını, kapanıştan sonra ise v1 isteğinin 410 aldığını görüyorsunuz. Kullanımdan kaldırma döneminde v1'in yanıt gövdesi hiç değişmedi; RFC 9745'in "davranış değişmez" ilkesi tam olarak budur.
İstemci tarafında başlıkları okumak
Başlıkları dikkate alan bir istemci, iki tarihi ayrıştırıp kayda geçirebilir, kural ihlalini denetleyebilir ve kalan süreyi hesaplayıp ekibini uyarabilir. Standart kütüphane iki biçimi de okumaya yeter:
from datetime import datetime, timezone
from email.utils import parsedate_to_datetime
basliklar = {"Deprecation": "@1796083200", "Sunset": "Tue, 01 Jun 2027 00:00:00 GMT"}
kalkis = datetime.fromtimestamp(int(basliklar["Deprecation"].lstrip("@")), tz=timezone.utc)
kapanis = parsedate_to_datetime(basliklar["Sunset"])
print("kullanımdan kalkış:", kalkis.isoformat())
# çıktı: kullanımdan kalkış: 2026-12-01T00:00:00+00:00
print("kapanış:", kapanis.isoformat())
# çıktı: kapanış: 2027-06-01T00:00:00+00:00
print("tutarlı mı:", kapanis >= kalkis)
# çıktı: tutarlı mı: True
print("kalan gün:", (kapanis - datetime(2026, 11, 7, tzinfo=timezone.utc)).days)
# çıktı: kalan gün: 206
parsedate_to_datetime, HTTP-date değerini saat dilimi bilgisi olan bir datetime nesnesine çevirir. Deprecation değeri ise @ işareti atılıp saniye olarak okunur. Gerçek bir istemcide bu kontrolü merkezi HTTP katmanına koyup başlığı gördüğünüzde bir uyarı kaydı üretmek, ekibin emeklilik takvimini kaçırmamasını sağlar.
OpenAPI belgesinde işaretlemek
Başlıklar çalışma anında bilgi verir; belgeler ise geliştiricinin kodu yazarken gördüğü yerdir. OpenAPI 3.1.1 belirtimi, işlem (operation) düzeyinde deprecated alanını tanımlar: true olduğunda kullanıcıların o işlemi kullanmaktan kaçınması beklenir. Yanıt başlıklarını da belgeye ekleyebilirsiniz. Aşağıdaki belge bir OpenAPI doğrulayıcısından geçirildi:
openapi: 3.1.1
info:
title: Kullanıcı API
version: "1.0"
paths:
/v1/kullanicilar/{kid}:
get:
summary: Kullanıcıyı getir (v1)
deprecated: true
description: v1 1 Aralık 2026'da kullanımdan kalkar, 1 Haziran 2027'de kapanır. /v2 kullanın.
parameters:
- name: kid
in: path
required: true
schema:
type: integer
responses:
"200":
description: Kullanıcı bulundu
headers:
Deprecation:
description: Kullanımdan kalkış tarihi (RFC 9745)
schema:
type: string
examples:
- "@1796083200"
Sunset:
description: Kapanış tarihi (RFC 8594)
schema:
type: string
examples:
- Tue, 01 Jun 2027 00:00:00 GMT
"410":
description: Sürüm kapatıldı
Belge yazımının ayrıntısı OpenAPI 3.1 YAML yazısında, FastAPI'nin otomatik belgesi OpenAPI ve Swagger yazısında anlatılıyor.
Kapanıştan sonra: 410 mu 404 mü?
RFC 9110, 410 Gone kodunu hedef kaynağa erişimin artık mümkün olmadığı ve bu durumun büyük olasılıkla kalıcı olduğu durumlar için tanımlar; sunucu durumun kalıcı olup olmadığını bilmiyorsa 404 Not Found kullanılmalıdır. RFC 8594'ün kendi örneği de kapanış sonrasında 410 dönülebileceğini, sunucu eski adresleri takip etmiyorsa daha genel bir 404 görülebileceğini söyler. Bilinçli olarak kapattığınız bir sürüm için 410 daha açıklayıcıdır ve gövdede yeni sürüme yönlendiren kısa bir mesaj vermek istemciye yol gösterir:
HTTP/1.1 410 Gone
Content-Type: application/json
{"detail": "v1 kapatıldı, /v2 kullanın"}Hata gövdesini standart bir biçimde vermek isterseniz Problem Details (RFC 9457) yazısına bakın. Kapanıştan sonra hangi kodun döneceğini de emeklilik politikası belgesine yazmak, RFC 8594'ün politika bağlantısı için önerdiği içeriklerden biridir.
Örnek bir emeklilik takvimi

RFC'ler süreleri belirlemez; politika belgede sizin tarafınızdan tanımlanır. Aşağıdaki tablo bu yazıdaki örnek tarihlerle bir v1 emekliliğinin adımlarını gösterir:
| Adım | Tarih (örnek) | Yanıtlarda ne görünür |
|---|---|---|
| v2 yayımlanır, politika duyurulur | 7 Kasım 2026 | Link rel="sunset" politika bağlantısı |
| v1 kullanımdan kalkar | 1 Aralık 2026 | Deprecation, Sunset ve Link rel="deprecation" |
| Geçiş dönemi | Aralık 2026 - Mayıs 2027 | v1 aynı davranışla çalışır, başlıklar sürer |
| v1 kapanır | 1 Haziran 2027 | 410 Gone ve yeni sürüme yönlendiren mesaj |
Sunset önbellek bilgisi değildir
RFC 8594, Sunset başlığının HTTP önbelleklemesiyle karıştırılmaması gerektiğini ayrıca vurgular. Önbellekleme bir temsilin ne zaman yeniden kullanılabileceğiyle ilgilenir; Sunset ise kaynağın durumu hakkında hiçbir şey söylemez, yalnızca kaynağın ne zaman erişilemez olabileceğini bildirir. İkisi birbirini tamamlar. Önbellek başlıkları Cache-Control ve ETag yazısında anlatılıyor. RFC ayrıca bir güvenlik notu düşer: kapanış tarihi, örneğin bir kaydın son kullanma tarihini veya oluşturulma zamanını ele verebilir; bu bilgiyi yanıt gövdesinde gösterseydiniz nasıl koruyacaksanız başlıkta da öyle koruyun.
Sık yapılan hatalar
- Sunset tarihini Deprecation tarihinden önceye koymak: RFC 9745 bunu açıkça yasaklar.
- Tarih biçimlerini karıştırmak: Deprecation için
@ile Unix zamanı, Sunset için IMF-fixdate kullanılır. - İlk aşamada yalnızca Sunset göndermek: Sunset kapanış anı içindir; "artık önerilmiyor" bilgisi Deprecation ile verilir.
- Kullanımdan kaldırırken davranışı değiştirmek: Deprecation döneminde kaynak eskisi gibi çalışmalıdır.
- Yalnızca başlıklara güvenmek: Başlıkları okumayan istemciler duyuruyu görmez; belgede ve sürüm notlarında de duyurun.
- Bağlantıyı HTTP üzerinden vermek: Geçiş belgesi bağlantılarını HTTPS ile sunun.
- Kapanıştan sonra anlamsız hata: 500 veya boş yanıt yerine 410 ve yeni sürümü gösteren bir mesaj verin.
Sık Sorulan Sorular
Deprecation başlığı bir standart mı?
Evet. RFC 9745 Mart 2025'te standartlar yolunda (Standards Track) yayımlandı ve başlık IANA HTTP alan adları kayıt defterine kalıcı olarak eklendi.
Deprecation ile Sunset arasında ne kadar süre olmalı?
RFC'ler bir süre belirlemez. Süreyi istemcilerinizin geçiş kapasitesine göre seçin, emeklilik politikası belgesinde yazın ve rel="sunset" bağlantısıyla duyurun.
Sunset tarihi geçtiyse ama kaynak hâlâ çalışıyorsa ne olur?
Başlık bir ipucudur, garanti değildir. RFC 8594 geçmiş bir tarihi "her an kapanabilir" olarak yorumlamayı önerir; istemciler geçişi ertelememelidir.
Tek bir alanı kullanımdan kaldırmak için bu başlıklar uygun mu?
Başlıklar kaynak düzeyinde çalışır, tek bir JSON alanını işaretlemez. Alan düzeyindeki değişiklikleri belgede duyurmak ve önce genişlet, sonra daralt yaklaşımıyla yeni sürüme taşımak daha uygundur.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: REST API tasarımı öğrenme rehberi · API versiyonlama: URL ve header · Breaking change ve geriye uyumluluk · Problem Details ile hata yanıtı · HATEOAS ve Link başlığı