Publicar una pàgina i esperar que un cercador hi passi a mirar és el comportament per defecte de gairebé qualsevol web, i ningú no es planteja que pugui ser a l'inrevés. IndexNow ho gira: avises tu, al moment. Veurem quins motors el consumeixen, què convé avisar i què no, i per què la part difícil no és la URL que entra a l'índex sinó la que en surt. Els exemples van en Laravel, que és on ens movem cada dia a AndorraDev, però el protocol és una petició HTTP i funciona igual des de qualsevol stack.
Google no hi participa, així que això suma al teu sitemap i no el substitueix
El primer de tot, perquè condiciona les expectatives: Google no és a IndexNow. Els motors que sí que el consumeixen són Bing, Yandex, Seznam i Naver, i un únic enviament al punt d'entrada compartit arriba a tots.
Dit això, continua valent la pena, i per dos motius que no són el volum de trànsit de Bing.
Bing alimenta més coses de les que sembla. Els assistents conversacionals que citen fonts estiren del seu índex, i allà la frescor importa molt més que en una cerca tradicional.
I el model canvia de direcció. Un sitemap és passiu: diu què existeix i espera que algú hi vagi. IndexNow és actiu: avises tu en el moment del canvi. Un lloc amb contingut que caduca (fitxes que s'exhaureixen, ofertes que expiren, articles que es despubliquen) té un desfasament entre el que mostra i el que el cercador es pensa que mostra, i aquest desfasament és el que això tanca.
El protocol sencer és un fitxer i un POST
No cal estudiar-se res. Publiques una clau al teu domini i envies les URL que han canviat.
POST https://api.indexnow.org/indexnow
{
"host": "eldomini.com",
"key": "a3f9c1...",
"urlList": ["https://eldomini.com/pagina-que-ha-canviat"]
}
Dos detalls del protocol que convé conèixer:
Una sola URL pot anar per GET amb la clau a la cadena de consulta, i diverses van per POST. Canviar de forma segons el nombre no és un caprici, és el que admet el protocol i estalvia una petició amb cos quan no cal.
El màxim són deu mil URL per petició. Per sobre d'això cal trossejar, i aquest trossejat hauria de ser automàtic: ningú vol descobrir el límit en producció.
La clau és pública expressament, i aquí hi ha el detall que ho trenca tot
La clau d'IndexNow no és un secret. És un fitxer que qualsevol pot llegir al teu domini, i la seva funció no és autenticar sinó demostrar que qui envia les URL controla el lloc.
Això té una conseqüència pràctica molt concreta: la clau que envies a la petició i el contingut del fitxer han de coincidir. Si no coincideixen, el motor rebutja l'enviament.
I aquí apareix el problema de desplegament. Si la clau viu en una variable d'entorn i el fitxer es serveix llegint-la, un servidor a qui li falti aquesta variable serveix un fitxer que ja no casa, i res no t'ho diu fins que un enviament és rebutjat. Per això convé tenir una ordre que comprovi el fitxer de debò, descarregant-lo per HTTP i comparant-lo amb la clau configurada. És la primera cosa que cal executar quan alguna cosa no surt.
Hi ha una decisió més que val la pena pensar: servir el fitxer com a ruta o com a fitxer físic. Com a ruta és més còmode i multidomini. Com a fitxer físic té un avantatge que només es veu el dia que el necessites: artisan down respon 503 a totes les rutes, i un motor que comprovi la teva clau enmig d'un desplegament conclouria que ha desaparegut.
L'amfitrió permès surt de la teva URL base, i aquest filtre és precisament el que impedeix que un entorn de proves acabi avisant d'URL de producció. Comprova que la variable apunta al domini correcte a cada entorn i tindràs aquella xarxa posada de sèrie, sense configurar res més.
La part que gairebé ningú implementa és la URL que surt de l'índex
Gairebé totes les integracions avisen en publicar i s'hi aturen, perquè és l'exemple que porta el protocol i és el cas que a qualsevol se li acut primer. Però un índex desactualitzat poques vegades molesta pel que li falta: molesta pel que li sobra, i això només es corregeix avisant també del que desapareix.
Avisar quan publiques és l'obvi i ho fa tothom. El que gairebé ningú fa és avisar quan despubliques, i és igual d'important: si retires una fitxa i no ho dius, el cercador continua ensenyant un resultat que porta a una pàgina que ja no existeix.
I hi ha un tercer cas que s'oblida sempre: quan canvia el slug. Allà cal enviar dues URL, la nova perquè la indexi i la vella perquè la revisi i la descarti. Si només envies la nova, l'antiga es queda a l'índex durant setmanes.
Això significa que la condició no és "està publicat", sinó una transició: què era abans i què és ara. Entra a l'índex, en surt, o canvia d'adreça.
Un observer ingenu t'envia cinquanta mil URL de cop
Enganxar l'avís a l'esdeveniment de desat és la primera línia que escriu tothom, i aguanta sense una esgarrapada mentre els canvis els fa una persona des d'un formulari. El que trenca el muntatge és el dia que els fa una màquina, perquè a la base de dades li és igual qui escrigui.
Si enganxes l'avís a l'esdeveniment de desat sense més, qualsevol procés que recorri la taula el dispara. I tot projecte amb volum en té algun: una ordre nocturna que recalcula estadístiques, una reindexació, una migració de dades.
En un catàleg nostre amb 157.000 fitxes, una ordre d'estadístiques desa cada registre per actualitzar uns comptadors. Sense cap precaució, aquella ordre hauria enviat de cop les més de cinquanta mil fitxes indexables que encara no s'havien avisat mai.
La solució és declarar quines columnes no compten com a canvi de contingut:
IndexNow::track(Game::class)
->url('games.show')
->when('published')
->ignoring([
'gameplay_count_cache', 'stats_cache', 'rating_avg',
'updated_at', 'last_played_at',
]);
Tretze columnes en aquell cas, totes comptadors que reescriu un procés automàtic. Amb la llista posada, aquella ordre no genera ni un sol avís.
I el criteri general és fàcil d'aplicar: si una columna l'escriu un procés i no una persona, no és contingut.
Una marca a la fila fa que l'enviament sigui idempotent
Una transició passa una sola vegada i no torna. Si l'avís que l'acompanyava es perd, no hi ha manera de recuperar-lo preguntant-ho a la base de dades, perquè una taula només sap com són les coses ara, mai com hi han arribat.
Detectar la transició exacta funciona, però exigeix estar mirant en el moment just. Si el procés falla, si l'avís es perd, o si la fila va canviar d'estat per una via que no dispara esdeveniments, aquella transició no torna a passar mai.
Desar una columna del tipus index_now_sent canvia la pregunta: en comptes de "s'acaba de publicar?", passa a ser "està publicat i encara no ho hem avisat?". Això és un estat, no un instant, així que es pot comprovar quan vulguis i avisar es torna idempotent.
I dona de franc l'ompliment retroactiu, que és el que necessites el dia que instal·les això sobre un lloc que ja té contingut: una ordre que recorre la taula en lots, envia i marca, i que si s'interromp pot continuar on anava.
El 202 és un èxit i el 403 gairebé sempre significa el mateix
IndexNow respon amb codis HTTP corrents però els dona significats propis, i no n'acompanya cap d'una explicació: reps el número i poc més. Val la pena tenir l'equivalència al davant la primera vegada, perquè dos d'ells semblen exactament el contrari del que són.
202 és correcte. Significa acceptat amb la clau encara en verificació, i és el normal les primeres vegades. Tractar-lo com a error és el malentès més comú.
403 gairebé sempre és el fitxer de clau. No és que t'hagin bloquejat: és que la clau no es pot llegir públicament o no coincideix. Un missatge d'error útil aquí hauria de dir-te la URL exacta on ha d'estar el fitxer i què ha de contenir.
422 significa que les URL no són totes del mateix amfitrió que vas declarar, o que la clau no correspon a aquell amfitrió.
429 i els 5xx sí que es reintenten, i el 429 porta el seu propi Retry-After que cal respectar.
400 no es reintenta, perquè una URL malformada no s'arregla sola.
Aquella distinció entre el que es reintenta i el que no és la meitat de la feina d'una integració seriosa. L'altra meitat és l'espera creixent entre reintents, de l'ordre d'un minut, cinc i quinze, amb l'últim intent deixant la fallada registrada amb el seu motiu real.
Els esdeveniments d'Eloquent no veuen el query builder
Hi ha un forat en qualsevol integració construïda sobre observers, i no el tapa cap paquet perquè no està a la seva mà: Eloquent només emet esdeveniments quan l'escriptura passa pel model. Tot el que va directe a la base de dades és invisible.
Aquestes quatre operacions no disparen esdeveniments de model:
Post::where('status', 'draft')->update(['status' => 'published']);
Post::where('created_at', '<', $data)->delete();
DB::table('posts')->insert($files);
Post::upsert($files, ['slug']);
Són escriptures directes contra la base de dades i cap observer se n'assabenta. Si el teu procés d'importació fa servir upsert, aquelles pàgines mai s'avisaran per la via automàtica.
La resposta correcta és doble: fes servir l'ordre d'ompliment després d'aquells processos, o, si tens la columna de marca, posa-la a zero al mateix UPDATE i deixa que el flux normal les reculli a la passada següent. La segona és més elegant perquè no requereix recordar res.
Què hi ha al mercat i en què es diferencia
Hi ha quatre camins, i el que els separa no és la qualitat de la implementació sinó quina part del problema s'assumeixen. Tots resolen bé l'enviament; gairebé cap no es fica en la decisió de quan cal enviar, que és on hi ha la feina de debò.
Els clients PHP purs et donen la petició HTTP i prou. Buffering, cua, gestió de la clau, agrupació per amfitrió i trossejat els escrius tu.
ymigval/laravel-indexnow i laravel-freelancer-nl/laravel-index-now són les dues referències de Laravel. La segona encua amb un retard configurable i fa servir unicitat de tasca per no repetir. Totes dues resolen bé el "com enviar". La diferència d'enfocament que convé valorar és que el "quan enviar" continua sent teu: crides l'enviament des d'on toqui, i decidir quina escriptura de quin model mereix un avís, i molt en particular les URL que surten de l'índex, és la feina que queda.
Fer-ho a mà són quinze línies i és perfectament raonable si tens vint pàgines. Amb volum, el que descobreixes pel camí és la ruta del fitxer de clau, l'agrupació per amfitrió, el trossejat a deu mil, el 202 que sembla error, el 403 que significa una altra cosa, el Retry-After, la deduplicació i l'ordre nocturna que t'envia cinquanta mil URL.
I l'API de Bing Webmaster Tools existeix, amb la seva quota i la seva clau pròpia. Cobreix només Bing, mentre que un enviament a IndexNow arriba als quatre motors.
El que va decidir el nostre cas va ser enganxar el protocol als esdeveniments d'Eloquent amb condicions de transició declaratives, perquè publicar avisi i despublicar també, sense haver de recordar-ho a cada lloc del codi.
I si no treballes amb Laravel
Els criteris no depenen del llenguatge:
- Avisa també quan la pàgina surt de l'índex, i envia la URL vella quan canviï el slug.
- Decideix quines columnes compten com a contingut. Si no ho fas, el teu primer procés massiu es converteix en un enviament massiu.
- Desa un estat, no depenguis d'enxampar l'instant. Converteix l'avís en idempotent i et dona l'ompliment retroactiu.
- Agrupa i espera una mica abans d'enviar. Una petició que canvia trenta pàgines hauria de produir un enviament, no trenta.
- I llegeix bé els codis: 202 és èxit, 403 és el teu fitxer de clau, i només 429 i els 5xx mereixen reintent.
On és el codi
El paquet està publicat a Packagist, el codi a GitHub i la documentació del paquet recull les condicions de seguiment, les ordres i la configuració. És MIT, amb sis ordres de consola i una taula opcional per a l'històric d'enviaments.
Si tens un lloc amb contingut que entra i surt de l'índex, això s'instal·la en una tarda i tanca el desfasament entre el que mostres i el que el cercador es pensa que mostres. Ho treballem a posicionament web i desenvolupament Laravel, i si ens expliques què publiques i amb quina freqüència et diem si et compensa.