IndexNow ile yeni ve değişen sayfaları arama motorlarına bildirmek

Anahtar dosyası, tek istekte toplu bildirim, hangi motorların desteklediği ve Google için neden hâlâ site haritası ile Search Console gerektiği.

Yazar: TS SystemLabs mühendislik ekibi · Yayın: · 6 dk okuma

Bir sayfayı yayınladığınızda arama motorlarının onu ne zaman fark edeceği büyük ölçüde onların tarama takvimine bağlıdır. Site haritası bu takvime yardım eder ama bir çağrı değildir; motor haritayı kendi uygun gördüğü sıklıkta okur. IndexNow bu ilişkiyi tersine çeviren açık bir protokoldür: yeni, değişen ya da silinen adresleri siz bildirirsiniz, katılan arama motorları da bildirimi birbirleriyle paylaşır.

Bu yazıda IndexNow'u kendi sitelerimizde nasıl kurduğumuzu anlatıyoruz: anahtar dosyasını Flask'ta yayınlamak, adresleri tek istekte toplu göndermek, yanıt kodlarını doğru okumak ve en önemlisi neyi ne zaman bildirmek gerektiği. Google'ın bu tabloda nerede durduğunu da açıkça yazacağız, çünkü en sık karşılaştığımız yanlış anlama bu konuda.

Protokol kısaca nasıl çalışır?

IndexNow'un üç parçası var. Birincisi bir anahtar: sizin ürettiğiniz, harf, rakam ve tire içeren, 8 ile 128 karakter arası bir metin. İkincisi bu anahtarın sitenizde yayınlandığı bir anahtar dosyası: genellikle kök dizinde /<anahtar>.txt adresinde duran ve içinde yalnız anahtarın kendisi yazan düz metin dosyası. Üçüncüsü bildirim isteği: hangi adreslerin değiştiğini söyleyen bir HTTP isteği.

Arama motoru bildirimi aldığında anahtar dosyasını sizin alan adınızdan çeker ve içindeki anahtarın istekteki anahtarla aynı olduğunu doğrular. Böylece yalnız alan adını gerçekten kontrol eden biri o alan adı için bildirim gönderebilir. Anahtar gizli bir parola değildir, zaten herkese açık bir dosyada durur; görevi sahipliği kanıtlamaktır.

Protokole katılan motorlar arasında Bing, Yandex, Seznam ve Naver bulunuyor; güncel liste indexnow.org sitesinde yayınlanıyor. Bir motora gönderilen bildirim diğer katılımcılarla paylaşıldığı için tek bir uç noktaya göndermek yeterli. Biz ortak uç nokta olan api.indexnow.org adresini kullanıyoruz.

Anahtar dosyasını Flask'ta yayınlamak

Anahtarı depoya bir dosya olarak koymak yerine ortam değişkeninde tutuyoruz. Böylece aynı kod farklı sitelerde farklı anahtarlarla çalışabiliyor ve anahtarı değiştirmek için yeni bir sürüm çıkarmak gerekmiyor. Anahtarı Python'la üretmek tek satır:

py -3.12 -c "import secrets; print(secrets.token_hex(16))"

Bu komut 32 karakterlik onaltılık bir metin üretir; protokolün izin verdiği karakter kümesinin ve uzunluk sınırının içinde kalır. Dosyayı yayınlayan rota ise şöyle:

import os

from flask import Flask, Response, abort

app = Flask(__name__)


@app.get("/<anahtar>.txt")
def indexnow_anahtari(anahtar):
    """INDEXNOW_KEY ortam değişkeni → /<anahtar>.txt; değişken boşsa ya da ad tutmazsa 404."""
    k = (os.environ.get("INDEXNOW_KEY") or "").strip()
    if k and anahtar == k:
        return Response(k + "\n", mimetype="text/plain")
    abort(404)

İki ayrıntıya dikkat edin. Değişken tanımlı değilse uç tamamen kapalı kalıyor; böylece yerel geliştirme ortamında ya da yanlış yapılandırılmış bir sunucuda rastgele .txt adresleri 200 dönmüyor. İkincisi, Flask'ın yönlendirici sırası: değişken içermeyen kurallar, değişkenli kurallardan önce eşlenir. Bu yüzden /robots.txt ya da /llms.txt gibi kendi rotası olan dosyalar bu genel kuraldan etkilenmez. Yine de bu davranışı bir testle kilitlemeniz iyi olur.

Tek istekte toplu bildirim

Tek bir adres için basit bir GET isteği de kullanılabilir, ancak birden fazla adres varsa JSON gövdeli bir POST isteği daha uygun. Gövdede alan adı (host), anahtar (key), anahtar dosyasının tam adresi (keyLocation) ve adres listesi (urlList) bulunur. Protokol tek istekte en çok 10.000 adrese izin veriyor. keyLocation, dosya kök dizindeyse isteğe bağlıdır; ama açıkça yazmak yanlış anlaşılma ihtimalini ortadan kaldırıyor. Listedeki bütün adreslerin istekteki alan adına ait olması gerekir.

Bizim ilk betiğimiz canlı site haritasını okuyup içindeki bütün adresleri gönderiyordu. Sadeleştirilmiş ve hata yönetimi eklenmiş hâli şöyle:

import json
import os
import re
import sys
import urllib.error
import urllib.request

SITE = (os.environ.get("SITE_URL") or "https://ornek.com").rstrip("/")
KEY = (os.environ.get("INDEXNOW_KEY") or "").strip()


def bildir(adresler):
    govde = json.dumps({
        "host": SITE.split("//", 1)[1],
        "key": KEY,
        "keyLocation": f"{SITE}/{KEY}.txt",
        "urlList": adresler[:10000],
    }).encode("utf-8")
    istek = urllib.request.Request(
        "https://api.indexnow.org/indexnow", data=govde,
        headers={"Content-Type": "application/json; charset=utf-8"})
    try:
        with urllib.request.urlopen(istek, timeout=60) as yanit:
            return yanit.status, ""
    except urllib.error.HTTPError as e:
        return e.code, e.read()[:200].decode("utf-8", "replace")


if __name__ == "__main__":
    if not KEY:
        sys.exit("INDEXNOW_KEY boş")
    adresler = sys.argv[1:]
    if not adresler:
        xml = urllib.request.urlopen(SITE + "/sitemap.xml", timeout=60).read().decode("utf-8")
        adresler = re.findall(r"<loc>([^<]+)</loc>", xml)
    kod, ayrinti = bildir(adresler)
    print("IndexNow", kod, len(adresler), "adres", ayrinti)

Betik, komut satırından adres verilirse yalnız onları, verilmezse site haritasındaki adresleri gönderiyor. urllib standart kitaplıkta olduğu için ek bağımlılık gerekmiyor; bu, sunucuda bir zamanlanmış görev olarak çalıştırmayı kolaylaştırıyor.

Yanıt kodlarını doğru okumak

IndexNow belgelerinde tanımlanan yanıt kodları, sorunun nerede olduğunu oldukça açık söyler:

  • 200: bildirim alındı.
  • 202: bildirim kabul edildi ama anahtar doğrulaması henüz tamamlanmadı. İlk gönderimde sık görülür; hata değildir.
  • 400: istek biçimi geçersiz; çoğunlukla bozuk JSON ya da eksik alan.
  • 403: anahtar geçerli değil; anahtar dosyası bulunamadı ya da içindeki anahtar istektekiyle aynı değil.
  • 422: adresler istekteki alan adına ait değil ya da anahtar beklenen biçimde değil.
  • 429: çok fazla istek; motor bunu olası istenmeyen gönderim olarak görüyor.

403 yanıtının tipik nedenleri yapılandırmayla ilgilidir: anahtarın ortam değişkenine baştaki ya da sondaki bir boşlukla yazılması (yukarıdaki kod bu yüzden strip() kullanıyor), ya da sitenin www'lu ve www'suz adreslerinin farklı davranması. Bu yüzden ilk iş anahtar dosyasını tarayıcıda, istekte yazdığınız keyLocation adresinden açıp içeriğini göz ile kontrol etmek. Alan adı ayrıntısı da önemli: bildirdiğiniz adresler kanonik alan adınızda olmalı. Sitenizin tek bir adrese yönlendiğinden emin olun; aksi hâlde hem IndexNow hem de arama sonuçları açısından aynı içeriğin iki kopyasını yayınlamış olursunuz.

Ne zaman bildirmeli, ne zaman bildirmemeli?

Protokolün amacı değişikliği haber vermektir; "sitem hâlâ burada" demek değil. İlk kurulumda site haritasının tamamını göndermek makul, ama sonrasında yalnız gerçekten yeni, değişen ya da kaldırılan adresler gönderilmeli. Bunu kendi projelerimizden birinde zor yoldan öğrendik: düzenli çalışan bir görev, içerik değişmese bile site haritasındaki bütün adresleri tekrar tekrar bildiriyordu. Bu, motorlara hiçbir yeni bilgi vermeyen anlamsız bir yük yaratıyordu ve 429 yanıtı riskini de büyütüyordu. Çözüm, son gönderimin durumunu saklayıp yalnız farkı göndermekti.

Site haritanızdaki lastmod değerleri doğruysa farkı bulmak kolay: önceki gönderimde hangi adresin hangi tarihle gönderildiğini bir dosyada tutar, yeni haritayla karşılaştırırsınız.

import json
import re
import urllib.request
from pathlib import Path

DURUM = Path("indexnow_durum.json")


def sitemap_oku(adres):
    xml = urllib.request.urlopen(adres, timeout=60).read().decode("utf-8")
    return dict(re.findall(r"<loc>([^<]+)</loc>\s*<lastmod>([^<]+)</lastmod>", xml))


def degisenler(simdiki):
    onceki = json.loads(DURUM.read_text("utf-8")) if DURUM.exists() else {}
    return [adres for adres, tarih in simdiki.items() if onceki.get(adres) != tarih]


def durumu_kaydet(simdiki):
    DURUM.write_text(json.dumps(simdiki, ensure_ascii=False, indent=1), "utf-8")

Akış şu: haritayı okuyun, değişenleri bulun, liste boş değilse bildir() ile gönderin ve yalnız 200 ya da 202 yanıtı geldiyse durumu kaydedin. Böylece başarısız bir gönderim bir sonraki çalıştırmada tekrar denenir. Silinen sayfaları da bildirmek isterseniz önceki durumda olup yeni haritada olmayan adresleri listeye eklemeniz yeterli; motor o adresi ziyaret ettiğinde 404 ya da 410 görecektir.

Bu yaklaşımın bir ön koşulu var: lastmod gerçekten içeriğin değiştiği günü göstermeli. Her istekte bugünün tarihini basan bir site haritası hem bu farkı anlamsız kılar hem de arama motorlarının bu alana güvenmemesine yol açar. Bu sitede ürün sayfalarının lastmod değeri o ürünün son güncelleme kaydından, yazıların değeri yazının kendi tarihinden geliyor.

Google için ne yapmalı?

Google IndexNow'a katılmıyor; IndexNow bildirimleri Google'a ulaşmaz. Google için yol hâlâ site haritası ve Search Console. Google'ın eski site haritası "ping" ucu da 2023'te kullanımdan kaldırıldı; artık haritayı Search Console'a bir kez eklemek ve robots.txt içinde Sitemap: satırıyla göstermek yeterli. Google, lastmod değerini ancak tutarlı ve doğru olduğu sürece dikkate aldığını belirtiyor; yani yukarıdaki dürüst tarih kuralı Google için de geçerli.

Tek bir önemli sayfayı hızlıca taratmak istediğinizde Search Console'daki adres denetimi aracından dizine ekleme isteyebilirsiniz; bu elle yapılan, sınırlı bir işlemdir ve toplu bildirim yerine geçmez. Pratikte düzenimiz şöyle: Bing, Yandex ve diğer katılımcılar için değişen adresleri IndexNow ile anında bildiriyoruz; Google için doğru lastmod taşıyan bir site haritası ve Search Console'daki kapsam raporlarını düzenli okumak. Çok dilli bir sitede site haritasının nasıl üretildiğini çok dilli site yazımızda anlattık.

Sonuç

IndexNow kurulumu birkaç satırlık bir iştir: ortam değişkeninden okunan bir anahtar, onu yayınlayan bir rota ve adresleri tek istekte gönderen küçük bir betik. Asıl fark, disiplinde ortaya çıkıyor. Yalnız değişen adresleri bildirmek, yanıt kodlarını okuyup başarısız gönderimi tekrar denemek, site haritasındaki tarihleri dürüst tutmak ve Google'ın bu protokolün dışında olduğunu unutmamak gerekiyor. Aynı düzeni Pati ve Ben ve StockFortuna gibi kendi ürünlerimizin statik içerik sayfalarında da kullanıyoruz; her birinde amaç aynı: arama motorlarına gürültü değil, gerçek değişiklik haberi vermek.

Benzer bir sistem mi kuruyorsunuz?

İhtiyacınızı birkaç cümleyle yazın; uygun bir yol varsa birlikte netleştirelim.

Bize yazın