Blog ·
Un badge che metteva un cookie su ogni pagina
Un’immagine nel piè di pagina costava ventidue punti Lighthouse su tutto il sito. Il rimedio non era toglierla, e conta ciò che un proxy deve rifiutare.
Di Provibr
Il punteggio delle buone pratiche del nostro sito di marketing era 78 su ogni pagina. Non su una pagina: su ogni pagina, la home, la pagina dei prezzi, la documentazione, tutto quanto, in modo uniforme. I punteggi uniformi sono un buon segno, perché vogliono dire che la causa sta in qualcosa che ogni pagina condivide.
Era un solo tag immagine nel piè di pagina. Un badge di una vetrina di lanci, caricato direttamente dalla piattaforma che lo emette, perché è così che un badge del genere va incorporato e perché il numero che porta dovrebbe essere vivo.
Che cosa può fare un tag immagine
Il badge viene servito da un host di API che sta dietro Cloudflare, e Cloudflare risponde alle richieste con il suo cookie di gestione dei bot: __cf_bm, SameSite=None, Secure, con ambito il dominio di chi emette il badge. Non è una decisione del sito di cui è il badge. È quello che fa una CDN, su un SVG, a ogni caricamento di pagina, per ogni visitatore.
Due controlli di Lighthouse reagiscono a questo. Il controllo sui cookie di terze parti pesa cinque dei ventotto pesi della categoria, e una segnalazione dell’ispettore pesa uno. Sei pesi persi su un’immagine. Sulle trentotto misure del turno precedente, era tutto lì il divario fra 78 e 100.
La terza conseguenza è quella che nessun punteggio cattura. L’indirizzo IP di ogni visitatore andava a un’azienda con cui non aveva scelto di parlare, su ogni pagina, in cambio di un numero in un piè di pagina.
Nemmeno toglierlo è la risposta
Il compromesso lo avevamo già annotato, in un commento, mesi prima: se mai lo si vuole senza un banner di consenso, ospitare l’SVG in proprio è la strada. Con la controargomentazione allegata, cioè che una copia ospitata in proprio è una fotografia. Il badge esiste per mostrare una posizione viva; congeli i byte e ha in mano l’immagine di una posizione.
C’è una terza via, ed è ovvia solo una volta che si separano due cose che il browser sta facendo contemporaneamente. Il browser sta prendendo dei byte, e il browser sta aprendo una connessione. Solo la seconda è il problema.
Il badge arriva dunque ora dalla nostra origine, a un percorso sulla nostra API, e quella rotta va a prendere la risorsa a monte sul server. Stessi byte. Il browser del visitatore fa una sola richiesta, a noi. Sul filo non c’è nulla su cui mettere un cookie, perché nella pagina del visitatore non c’è affatto una terza parte. E il numero resta vivo, perché continuiamo a chiedere.
Le buone pratiche sono passate da 78 a 100 su trentadue misure su trentasei. Le quattro rimaste sono le pagine degli articoli, che stanno a 96 per via di un pulsante di terze parti del tutto diverso, e quella è una decisione a sé e non un residuo.
Che cosa deve un proxy a chi lo attraversa
Servire i byte di qualcun altro dalla propria origine cambia che cosa sono. Un SVG caricato attraverso un tag immagine non può eseguire script; è una garanzia della specifica ed è una buona garanzia. Ma nel momento in cui quei byte vengono dal nostro dominio, quello che c’è dentro è nostro.
La rotta controlla dunque la risposta prima di passarla oltre. Quel controllo di proposito non è un igienizzatore. Un igienizzatore cerca di rendere sicuro un contenuto pericoloso; questo rifiuta tutto ciò che non somiglia al badge che già conosciamo, e la rotta ripiega su una copia congelata. Il caso peggiore è un badge di stamattina invece di un badge di questo minuto, ed è un prezzo che vale la pena pagare per non dover mai ragionare su che cosa possa contenere la risposta di qualcun altro.
Due dei rifiuti riguardano proprio la cosa per cui tutto questo cambiamento è stato fatto. Un SVG può fare riferimento a un’immagine esterna, e può fare riferimento a una risorsa esterna da una dichiarazione di stile. L’uno o l’altro andrebbe a cercare una terza parte uno strato più in profondità del tag che abbiamo appena tolto, da dentro un file che serviamo come nostro. Un controllo che cercasse soltanto script li avrebbe lasciati passare entrambi.
Fallire in un’immagine, di proposito
Ogni percorso di fallimento restituisce la copia congelata con stato 200. Errore di rete, timeout, uno stato diverso da 200, un corpo di cui il controllo non si fida: tutti quanti, 200, con dentro un badge.
Il ragionamento riguarda l’aspetto del fallimento. Un 404 o un 502 su un’immagine nel piè di pagina di ogni pagina è un’icona di immagine rotta su ogni pagina, che si legge come un sito rotto, per un elemento decorativo. Un badge un po’ vecchio non si legge come nulla.
La compensazione è che il percorso di fallimento porta un’intestazione di cache breve mentre il percorso riuscito ne porta una lunga. Altrimenti un guasto di cinque minuti a monte inchioderebbe la copia congelata davanti a tutti per l’intera finestra di cache.
C’è una sola copia congelata e di proposito non vive come file nella cartella pubblica. Come file avrebbe un secondo indirizzo pubblico accanto alla rotta, e un turno futuro potrebbe ragionevolmente piazzare un tag immagine che punta dritto a quel file, cosa che in silenzio toglie il comportamento vivo che tutta questa disposizione esiste per conservare. Tenuta come costante, l’unico modo di servirla è attraverso la rotta che prova prima anche la rete.
Due cache, che fanno due lavori diversi
Qui ci sono due strati di cache e confonderli produce un sistema che o martella il fornitore a monte o mostra byte stantii per sempre.
La cache dei dati del framework tiene la risposta a monte: con una rivalidazione di sei ore, un visitatore ogni sei ore provoca una richiesta in uscita, a prescindere dal traffico. L’intestazione di cache HTTP tiene la nostra risposta, alla CDN e nel browser. Il fornitore a monte mette quattro ore sulla propria risposta; sei dalla nostra parte sono di proposito un mezzo passo più larghe, così che un guasto laggiù non ci faccia cadere sulla copia congelata a ritmo di quattro ore.
Una piccola trappola, che è costata una build. Next legge la configurazione di segmento di rotta staticamente dal sorgente, quindi l’export della rivalidazione deve essere un numero letterale. Scriverlo come espressione, anche come costante dichiarata qualche riga più sotto nello stesso file, interrompe la build con un errore di configurazione di segmento non valida. La costante ora legge dall’export invece che il contrario, così c’è ancora esattamente un solo numero.
Il 307 che era un’opzione di avvio
Lo stesso turno ha passato del tempo su qualcosa che sembrava un difetto di instradamento e non lo era, e vale la pena registrarlo perché ha sprecato per intero un turno precedente.
Eseguendo la build di produzione in locale, ogni percorso di marketing nudo rispondeva 307, reindirizzando a sé stesso. Non i percorsi localizzati, non le rotte di API: quelli nudi. Questo aveva bloccato per settimane la metà inglese di ogni spazzata di impaginazione locale ed era stato annotato come un difetto inspiegato.
È l’aggancio all’host. Avviato con un indirizzo di loopback letterale come nome host, Next lascia quel valore com’è, mentre un nome host vuoto o jolly viene normalizzato a localhost. L’URL iniziale e l’origine che il middleware calcola non concordano più, la riscrittura resta assoluta, il router la tratta come esterna e rientra. Avviato con il jolly, che è il valore usato in produzione, oppure con localhost, tutto risponde 200.
Non c’era nulla da riparare. Quello che c’era da fare era scrivere la riga di avvio corretta nello script, e annotare il perché, perché altrimenti un difetto che in realtà è un’opzione verrà riscoperto dalla persona successiva con la stessa sicurezza.
Il guasto che abbiamo scelto di non riparare
Una terza cosa dello stesso turno è una non riparazione deliberata, e queste meritano di essere annotate almeno con la stessa cura delle riparazioni.
Avevamo visto dei 404 sui chunk sul campo: HTML che fa riferimento a file JavaScript che non esistono più. La spiegazione si è rivelata essere una sola intestazione. La home porta una finestra di stale-while-revalidate misurata in un anno, quindi a un nodo di bordo della CDN è permesso servire HTML di una build più vecchia per moltissimo tempo, e i nomi dei chunk in quell’HTML appartengono a quella build.
La manopola è lì e l’abbiamo lasciata stare, perché è proprio quella copia stantia che ha tenuto il sito a rispondere durante un ciclo di crash della banca dati quindici giorni prima. Accorciare la finestra baratta un raro caricamento rotto contro un guasto totale ancora più raro. Quello è un baratto vero, appartiene a chi possiede il rischio, e il compito del turno era spiegare il meccanismo e non prendere la decisione.
Che cosa annoteremmo
- Un punteggio uniforme su ogni pagina indica qualcosa che ogni pagina condivide. È la pista che Le costerà meno.
- Un tag immagine è una connessione, non solo byte. Tutto quello che una CDN vuole fare su una richiesta, lo fa anche su quella.
- Ospitare in proprio non è l’unica alternativa a incorporare. Prendere i byte sul server conserva il comportamento vivo e toglie la terza parte dalla pagina del visitatore.
- Una volta che serve i byte di qualcun altro, sono suoi. Li controlli, e controlli in particolare i riferimenti che riaprirebbero il buco appena chiuso.
- Preferisca un’immagine vecchia a un’immagine rotta. Per una decorazione un fallimento visibile costa più di un invecchiamento invisibile, purché sul percorso di fallimento la finestra di invecchiamento sia breve.
- Non dia un indirizzo pubblico alla copia congelata. Tutto ciò che è raggiungibile prima o poi viene collegato, e il percorso vivo verrà aggirato in silenzio.
- Sappia quale cache tiene che cosa. Una tiene la risposta a monte, una tiene la sua, e le loro finestre rispondono a domande diverse.
- Certi rossi sono un’opzione, non un difetto. Riproduca il meccanismo prima di riparare il sintomo, e se si rivela il suo stesso comando di avvio, lo scriva nello script.
Per chiarezza su che cosa sia: la rotta è in produzione e i punteggi vengono da una build di produzione locale su trentasei misure, non da dati sul campo. La spiegazione dei 404 sui chunk è derivata dalle intestazioni e non è stata riprodotta, e la decisione su quella finestra di cache appartiene a chi porta il rischio di disponibilità, non al turno.
Dal lavoro di piattaforma su Provibr. Un’altra misura diventata verde per la ragione sbagliata si trova in un’eccezione di accessibilità che si misura, e un campo che teneva un’intenzione dove ci voleva un fatto in una data futura non è un calendario.