Browserless è Chrome senza interfaccia grafica offerto come servizio che ospiti tu stesso. Il container Docker resta attivo, accetta richieste via HTTP o WebSocket e applica limiti condivisi di ammissione invece di essere integrato in ogni singolo chiamante. Nei test REST eseguiti qui, il servizio è rimasto con zero processi Chrome inattivi, ha creato processi Chrome mentre una richiesta era in corso ed è tornato a zero subito dopo. Ciò che viene condiviso è la capacità del servizio e la gestione della coda, non un insieme verificato di processi browser già avviati.
Ho provato la v2.55.0 su un fixture locale controllato — avvio scomposto in fasi, controllo dell’ammissione in tre configurazioni, verifica degli endpoint contro una ground truth nota, un test di tenuta da 30 sessioni e il limite di timeout. Il comportamento utile è stato operativo più che prestazionale: i limiti di ammissione corrispondevano alle risposte visibili dal client, e le asperità erano soprattutto legate al deployment.
Il risultato più utile non è stato un numero di latenza. Browserless non fa partire Chrome più velocemente: mette una policy di accesso davanti a Chrome — un numero fisso di sessioni all’interno, una coda dietro e un HTTP 429 per tutti gli altri. Quel tetto è passato da 4 a 8 a 10 quando ho modificato due variabili d’ambiente, e il conteggio interno del container ha coinciso con i codici di stato del client in ogni singola richiesta.
Cos’è davvero Browserless
Qui è facile confondersi. Browserless non è una libreria da importare e chiamare. È un’immagine Docker — ghcr.io/browserless/chromium — da eseguire come servizio a lunga durata. Fa da intermediario per i lavori del browser ed espone due interfacce: endpoint REST (/content, /scrape, /screenshot, /pdf, oltre a /function e /unblock) e una superficie CDP/WebSocket a cui Puppeteer e Playwright possono fare connect().
Ho testato la superficie REST. Il percorso WebSocket esiste ed è molto usato, ma non l’ho misurato.
La versione sotto test era v2.55.0, verificata il 27 luglio 2026.
| Voce | Valore |
|---|---|
| Versione immagine | v2.55.0, pubblicata il 14 luglio 2026 |
| Chrome | 149.0.7827.0 |
| Node | 24.18.0 |
| Immagine base | Ubuntu 24.04 |
| Star su GitHub | circa 13.525, al 27 luglio 2026 |
Il numero delle star cambia nel tempo; consideralo come una lettura puntuale.
Il vincolo di licenza: SSPL-1.0 o licenza commerciale
Il repository offre Browserless sotto SSPL-1.0 oppure una licenza commerciale Browserless. Prima di scegliere, leggi l’attuale file LICENSE del repository e la guida ufficiale al deployment open source di Browserless. Questo articolo non ha svolto un’analisi legale di prodotti commerciali, applicazioni closed source, sistemi CI, servizi ospitati o deployment interni, quindi non assegna una licenza a questi scenari. Fai valutare il modello di deploy e distribuzione da un legale o da chi si occupa di licensing software.
Browserless vende anche piani hosted. Prezzi e definizioni delle unità di utilizzo cambiano spesso e non facevano parte di questo test self-hosted, quindi verificali sul sito ufficiale invece di prendere una tabella datata qui come prova d’acquisto.
Come funziona davvero il modello di sessione
Il percorso REST misurato si comportava come lavoro browser per richiesta, ma questo harness non ha tracciato abbastanza a fondo l’interno di Browserless da distinguere un nuovo processo browser da qualunque strategia di riuso del contesto. Quello che ha stabilito è più semplice: zero processi Chrome a riposo, 11 processi della famiglia Chrome durante una richiesta attiva e zero al termine della sequenza. In questa configurazione non c’erano prove di un pool di browser già scaldati.
Il servizio Node a lunga durata accetta un numero limitato di job browser, ne mette in coda un altro numero limitato e rifiuta il resto. Considera questo modello di ammissione come il contratto architetturale misurato qui sotto. Non dedurre il riuso di processi o contesti dalla parola “pool”; l’harness temporale non può dimostrarlo.
Il controllo di ammissione ha due manopole:
CONCURRENT— quante sessioni girano contemporaneamente.QUEUED— quante richieste aggiuntive possono attendere un posto.
L’endpoint /config del container riportava i valori predefiniti CONCURRENT=10, QUEUED=10, TIMEOUT=30000. Qualsiasi cosa oltre CONCURRENT + QUEUED viene rifiutata all’istante.
L’autenticazione non è facoltativa. Browserless v2 richiede sempre un token — se non imposti TOKEN, ne genera uno casuale e lo stampa su stdout all’avvio. Ogni chiamata REST porta ?token=.
Per l’osservabilità hai /pressure (in esecuzione, in coda, CPU, memoria, rifiuti recenti), /sessions e /config. Esiste anche un export JSON /metrics, ma richiede METRICS_JSON_PATH e non l’ho usato. Inoltre l’immagine esegue dumb-init come PID 1, che è la risposta documentata alle lamentele sui processi zombie che da anni accompagnano Chrome containerizzato.
Realtà del setup: un comando e quattro cose che nessuno mette dentro quel comando
La riga di installazione che tutti citano è davvero un solo docker run. Tutto ciò che sta intorno è ciò che devi pianificare.
L’immagine pesa 4,34 GB. Questo è il dato che dovrebbe orientare le aspettative, non la latenza di avvio. Il manifest include sia linux/arm64 sia linux/amd64; sul mio host arm64 Docker ha scaricato la variante arm64 nativa. (L’user-agent di Chrome nel container continua però a leggere X11; Linux x86_64 — è l’UA cosmetico di Chrome su Linux, non emulazione. uname -m restituisce aarch64. Le persone aprono ticket su questa cosa.)
Ho usato --shm-size=2g in ogni misurazione. L’harness non includeva una run di controllo con /dev/shm predefinito di Docker, quindi questo articolo non può dire che 2 GiB siano universalmente obbligatori né quantificare il punto di rottura. Dimensiona in base al numero di browser e al workload.
Il token è un tema di deployment, non una formalità. Senza di esso, chiunque riesca a raggiungere la porta 3000 controlla un browser nella tua rete.
Il networking del container è un tuo problema. Il mio fixture girava sull’host, quindi il container lo raggiungeva tramite host.docker.internal (colima lo mappa con --add-host host.docker.internal:host-gateway). Ho verificato che il container raggiungesse davvero il fixture con un curl grezzo prima di fidarmi di qualunque misurazione.
Il mio ambiente: colima 0.10.3 (6 CPU / 11,6 GiB) con Docker 29.2.1 su macOS 26.5.2 arm64. L’harness era solo Python 3 stdlib. Per PNG e PDF ha controllato le firme dei file, non la validità del decoder, le dimensioni, il numero di pagine, la completezza o la fedeltà visiva.
Un avvio minimo equivalente usa l’immagine bloccata, un token esplicito e l’allocazione di memoria condivisa usata qui:
docker run --rm -p 3000:3000 --shm-size=2g \
-e TOKEN=replace-with-a-secret \
ghcr.io/browserless/chromium:v2.55.0
Dopo che /pressure?token=... risponde, un POST /content?token=... autenticato con un body JSON che contiene l’URL di destinazione esercita il percorso REST. Anche i chiamanti in produzione devono usare retry limitati con jitter per le risposte 429; riprovare subito significa solo competere di nuovo con la stessa coda piena.
Il costo di avvio, scomposto

Tre avvii freschi con docker run, mediane con min–max:
| Fase | Mediana | Intervallo | Cosa è davvero |
|---|---|---|---|
docker run → /pressure restituisce 200 | 0,78 s | 0,70–0,87 s | endpoint HTTP reattivo; l’avvio del browser non è verificato da questo controllo |
pronto → primo render /content | 0,32 s | 0,28–0,41 s | prima richiesta osservata: lavoro browser + navigazione + ritorno HTML |
chiamate successive a /content | 0,15 s | 0,147–0,154 s | latenza osservata nelle richieste successive nello stesso container |
La riga centrale è facile da interpretare troppo in fretta. Non misura l’avvio del browser in isolamento e non dimostra che Browserless avvii Chrome più velocemente di una libreria in-process. È un round trip HTTP verso un container più lavoro del browser, navigazione e trasferimento della risposta. Il divario di circa 0,17 secondi tra prima e successive richieste potrebbe includere effetti del filesystem, del sistema operativo, di Chrome, di Node o della cache del container. Poiché i processi Chrome erano a zero a riposo e l’harness non ha catturato alcun trace CDP o timeline dei processi per queste chiamate, non può attribuire quel divario al riuso del browser o al costo di avvio “ammortizzato”.
Inoltre: questi sono numeri di una VM colima su macOS. Un Linux bare metal darà risultati diversi. Non citare 0,78 s al tuo SRE come se fosse un valore portabile.
Pratica: trovare il tetto, da entrambi i lati
Il contratto CONCURRENT + QUEUED → 429 è ripetuto ovunque e dimostrato quasi mai.
La configurazione: una route fixture che dorme 5 secondi lato server, così ogni richiesta occupa in modo affidabile una sessione per una durata nota. Poi invia simultaneamente CONCURRENT + QUEUED + 4 richieste e guarda cosa torna — mentre un thread di campionamento separato interroga /pressure per leggere il conteggio interno del container.
| Config (CONCURRENT, QUEUED) | Richieste inviate | HTTP 200 | HTTP 429 | Picco server /pressure (running / queued / recentlyRejected) |
|---|---|---|---|---|
| (2, 2) | 8 | 4 | 4 | 2 / 2 / 4 |
| (3, 5) | 12 | 8 | 4 | 3 / 5 / 4 |
| (5, 5) | 14 | 10 | 4 | 5 / 5 / 4 |
Tre cose sono emerse da questo.
Il tetto è esattamente CONCURRENT + QUEUED, sempre. Le risposte riuscite sono state 4, 8 e 10 — la somma configurata in ogni caso. I rifiuti corrispondevano all’eccedenza, che è stata 4 in tutte e tre le run.
Il tetto si sposta. Non è un costante cucito nell’immagine; è ciò che configuri. Passare da 4 → 8 → 10 cambiando le variabili d’ambiente è la parte che rende tutto questo utile e non solo una curiosità.
E i due segnali sono indipendenti. I codici di stato del mio client provenivano da vere risposte HTTP; /pressure dal conteggio interno del container, interrogato da un thread diverso. In queste tre brevi run hanno coinciso. Questo rende /pressure un candidato segnale di produzione, non un contratto completo di autoscaling: cadenza di scraping, semantica di reset, aggregazione multi-replica e comportamento sotto carichi misti più lunghi richiedono ancora validazione.
Un aspetto che i conteggi pass/fail nascondono. Una richiesta in coda non fallisce — aspetta, e può aspettare a lungo. Con (2, 2) e lavoro da 5 secondi, le risposte riuscite sono arrivate tra 5,7 s e 11,0 s, mediana 8,3 s. La latenza end-to-end ha quindi raggiunto circa due durate di sessione. L’harness non ha catturato timestamp separati di ammissione ed esecuzione, quindi non può attribuire l’intero ritardo all’attesa in coda.
Come appare su un lavoro reale
Supponiamo tu debba renderizzare 4.000 pagine prodotto in PDF ogni notte, e ogni pagina richieda circa 5 secondi. Imposti CONCURRENT=5, QUEUED=5. Il tuo tetto di throughput è 5 pagine ogni 5 secondi — una pagina al secondo — quindi il job dura circa 67 minuti se tieni il tubo esattamente pieno. È aritmetica basata sul comportamento misurato, non un benchmark, ma è l’aritmetica che dovresti fare prima di andare in produzione.
Qualsiasi richiesta che arrivi quando tutte le slot in esecuzione e in coda sono occupate può ricevere subito un 429; l’invio simultaneo non garantisce quale richiesta in ordine perderà la corsa. Un job runner dovrebbe trattare questa risposta come backpressure e usare retry limitati con jitter. Altrimenti rischia di perdere pagine mentre il conteggio del job a livello superiore continua — un rischio operativo, non uno scenario di fallimento dimostrato da questo harness.
Pratica: cosa vedono davvero gli endpoint
Per testare onestamente la fedeltà del rendering, la pagina fixture nasconde il suo testo marcatore a chiunque non stia eseguendo un vero browser. La stringa visibile Runtime Injected Marker 88 viene assemblata da frammenti JavaScript al caricamento, quindi non esiste alcun letterale contiguo per essa in nessun byte inviato dal server. Un fetch statico semplice di quella pagina restituisce 702 byte che non contengono nessuno dei due marker.
| Endpoint | Risultato | Byte |
|---|---|---|
/content | marker iniettato a runtime presente, più entrambi i marker statici | 811 |
/scrape su #scrape-me (nodo iniettato da JS) | restituito SCRAPE_TARGET_VALUE_CC | 422 |
/screenshot | risposta con firma PNG 89 50 4E 47 | 18.621 |
/pdf | risposta con firma PDF %PDF- | 40.974 |
| tutti e quattro, senza token | HTTP 401 (non 403) | — |
Il fatto che /content restituisca 811 byte con il marker iniettato significa che un vero Chromium ha renderizzato la pagina prima che tornasse l’HTML. /scrape ha estratto un valore da un nodo che non esiste finché JavaScript non gira. Entrambi hanno funzionato con zero codice di automazione lato client — un singolo POST autenticato.
Questo è il vero valore. Nello stesso giro di test, un crawler statico ha perso del tutto questa classe di contenuti, e le librerie browser in-process (chromedp, rod, Selenium) l’hanno intercettata solo dopo che ho scritto un’attesa esplicita. Browserless l’ha presa con una richiesta “a forma di curl”. Stai scambiando codice di automazione con peso di deployment.
Due limiti a questa affermazione. Le evidenze coprono le classi di contenuto del mio fixture, non un sondaggio del web moderno. E /unblock, l’endpoint anti-rilevamento, è stato lasciato intenzionalmente da parte — nessuno di questi risultati va letto come una rivendicazione di capacità anti-bot. Anche /function, /download e /performance non sono stati testati.
Pratica: un breve controllo dei residui
Chrome containerizzato ha la reputazione di lasciare cadaveri dietro di sé, così ho eseguito 30 sessioni sequenziali con CONCURRENT=3 e contato i processi dentro il container.
Prima di fidarmi dei risultati, ho calibrato il rilevatore. Mentre una sessione era in corso, l’enumeratore di /proc leggeva 11 processi della famiglia Chrome (browser, zygote, GPU, renderer, utility). È importante: dimostra che lo strumento vede Chrome, quindi il suo zero post-run è una misura e non cecità. Un test di leak che restituisce “0 processi” senza provare di saper contare non vale nulla.
Dopo 30 sessioni: 0 processi chrome, 0 zombie. Gli unici sopravvissuti erano dumb-init, node, Xvfb, start.sh e sh. /sessions riportava 0 a riposo.
Memoria del container, da docker stats (il numero visibile all’operatore, non l’RSS di un singolo processo):
| Dopo N sessioni | 0 | 5 | 10 | 15 | 20 | 25 | 30 |
|---|---|---|---|---|---|---|---|
| Memoria container (MiB) | 294 | 300 | 301 | 302 | 302 | 303 | 303 |
Crescita netta su 30 sessioni: circa 9,5 MB, e la curva campionata si è appiattita dopo la sessione 10. Questo è incompatibile con un semplice leak lineare per-sessione in questa finestra breve. Il warmup di Node è una spiegazione plausibile, non qualcosa che questo conteggio di processi e questa serie di memoria possano provare.
Ambito: 30 sessioni sequenziali sono un piccolo soak, non una prova di resistenza né di concorrenza. I vecchi warning EventEmitter nel tracker delle issue sono il tipo di problema che può emergere dopo ore e migliaia di sessioni, e io non ho eseguito quel test. La conclusione supportata è solo che in questa finestra, su v2.55.0, non sono stati osservati processi Chrome o zombie in accumulo.
Il limite del timeout
TIMEOUT è documentato come una manopola. Volevo vedere quando scatta.
| Caso | Pagina trattenuta | Stato | Tempo trascorso |
|---|---|---|---|
| Sotto budget | 2.000 ms | 200 | 2,406 s |
| Oltre budget | 15.000 ms | 408 | 5,007 s |
Con TIMEOUT=5000, una sessione che cercava di trattenere una pagina per 15 secondi ha restituito HTTP 408 a 5,007 s invece di bloccarsi. Questa singola osservazione conferma l’applicazione del limite vicino al valore configurato. Non rivela l’implementazione del timer né prova la pulizia della slot; un test più forte ripeterebbe la prova, osserverebbe /sessions e /pressure tornare a idle e poi confermerebbe che una richiesta successiva acquisisce la slot liberata.
La trappola della migrazione: PREBOOT è silenzioso, e non te lo dice
Di tutto ciò che ho misurato, questo è il risultato che vorrei mi venisse consegnato prima di un upgrade.
Browserless 2.0.0 ha rimosso PREBOOT e KEEP_ALIVE — il changelog dice che sono stati eliminati perché confusi, poco utili e fonte di bug. Scelta ragionevole. Il problema nasce quando una configurazione v1 viene copiata e incollata su v2, che è il modo più comune in cui le persone fanno upgrade.
Ho eseguito il container con -e PREBOOT=true e l’ho confrontato con il comportamento predefinito:
| Segnale | PREBOOT=true | Default, flag non impostato |
|---|---|---|
| Tempo di readiness | 0,716 s | 0,776 s |
| Render a freddo | 0,314 s | 0,318 s |
| Render a caldo | 0,163 s | 0,150 s |
| Processi Chrome a riposo | 0 | 0 |
Ogni timing rientra nella banda min–max del ramo predefinito — è rumore, non un effetto. E nulla è stato pre-warmato: il container PREBOOT=true rimane inattivo senza browser in esecuzione, identico a uno senza quel flag. Altri due segnali, nessuno dei quali è un numero:
/confignon espone alcuna chiavepreboot. Le chiavi sonoconcurrent,queued,timeout,token,maxCPU,maxMemory,retriese simili.- Nessun errore. Nessun warning. Nulla nei log del container.
Quindi una configurazione v1 con PREBOOT su v2 è un no-op silenzioso durante l’avvio ordinario e i controlli dei log. Il segnale rilevabile è la chiave mancante in /config più il comportamento invariato; Browserless non emette un rifiuto esplicito né un warning. Un controllo di migrazione deve ispezionare la configurazione applicata invece di trattare un avvio verde come prova che ogni variabile d’ambiente abbia avuto effetto.
KEEP_ALIVE è il caso opposto, e i due non vanno confusi. È stato rimosso nella stessa release, ma non è silenzioso — un controllo puntuale del container mostra che registra Environment variable of "KEEP_ALIVE" is deprecated and ignored. proprio su stdout. Questo è un vero warning rivolto all’operatore. Non ho passato KEEP_ALIVE attraverso lo stesso harness misurato usato per PREBOOT, quindi lo riporto come controllo e non come misura. Ma la direzione è abbastanza chiara da contare: solo PREBOOT è la trappola silenziosa. Browserless è più onesto su KEEP_ALIVE di quanto lascerebbe intendere un generico “v2 ignora i flag v1”.
Pro e contro
Pro
- Controllo dell’ammissione che si comporta esattamente come documentato e si muove con la configurazione — dimostrato a tre tetti diversi, sia sui codici di stato del client sia sul conteggio interno del server.
/pressureha corrisposto ai conteggi visibili di running, queued e rejected in tre brevi run; valutalo come un possibile input per autoscaling e alerting.- Rendering reale di Chromium senza codice di automazione lato client: un solo POST autenticato ha esposto DOM iniettato da JS che un fetch statico della stessa pagina non può vedere.
- Nessun accumulo di processi Chrome osservato in una run sequenziale da 30 sessioni; il conteggio post-run era 0 processi Chrome e 0 zombie.
- Un trial di
TIMEOUTha restituito 408 a 5,007 s contro un budget di 5,000 s; cleanup e rilascio della slot non sono stati verificati separatamente. - Autenticazione attiva di default: tutti e quattro gli endpoint REST restituiscono 401 senza token.
- Un solo
docker runper arrivare a un servizio pronto in circa 0,78 s, e un primo render 0,32 s dopo.
Contro
- Immagine da 4,34 GB. È il costo iniziale reale, e si riflette nel registry, nella cache CI e nei tempi di cold deploy.
- SSPL-1.0 o licenza commerciale Browserless. Valuta i termini correnti esatti rispetto al tuo modello di deploy e distribuzione.
PREBOOTda v1 viene accettato e ignorato in silenzio su v2 — nessun errore, nessun warning, nessuna chiave/config.- Stai gestendo un servizio, non aggiungendo una dipendenza: un container, un token, un percorso di rete, un limite di ammissione e la responsabilità degli upgrade.
- Le richieste in coda hanno spinto la latenza end-to-end a circa due durate di sessione nella run
(2, 2); l’attesa in coda separata non è stata misurata. - Il timing REST misurato include un passaggio HTTP e non isola il costo di avvio del browser né prova il riuso del browser.
Chi dovrebbe usarlo e chi no
Browserless vale il suo peso quando più di una cosa ha bisogno di un browser. Un servizio di rendering condiviso fra più app, un team che vuole screenshot e PDF dietro un endpoint HTTP invece che una dipendenza Chrome in ogni servizio, una pipeline di job che ha davvero bisogno di un tetto di capacità con backpressure misurabile — è questa la forma che si adatta. Se hai già Docker e qualcuno si occupa del deploy, la storia operativa è chiara: ammissione prevedibile, backpressure osservabile e nessun accumulo di processi Chrome o zombie osservato nel controllo sequenziale da 30 sessioni.
È anche la scelta giusta se l’alternativa è far installare Chromium a ogni servizio dello stack. Centralizzare tutto in un container con token e tetto è davvero un buon compromesso architetturale.
Saltalo se stai scrivendo uno script singolo. Scaricare 4,3 GB e avviare un container solo perché un file Python possa prendere una pagina renderizzata è troppa cerimonia per un piccolo lavoro — una libreria browser nel tuo processo lo fa senza un servizio separato. Saltalo se i termini SSPL non sono compatibili con il tuo prodotto commerciale e non puoi risolvere la questione. Saltalo se ciò che vuoi davvero è un browser già caldo, senza costo a freddo, perché PREBOOT su v2 non te lo darà. E saltalo se il tuo vero problema è l’anti-bot, perché quella vive in un endpoint che ho volutamente non testato e su cui non mi sbilancio.
Alternative, incluso dove si colloca Thunderbit
Il confronto utile non è Browserless contro un altro container. È: dove vive il browser e chi è responsabile di tenerlo in vita.
Recensione correlata: recensione di Browsertrix Crawler.
Recensione correlata: recensione di chromedp.
| Libreria browser (chromedp, rod, Selenium, Playwright) | Browserless self-hosted | Estrattore gestito Thunderbit | |
|---|---|---|---|
| Dove gira il browser | Nel tuo processo | Nel tuo container | Infrastruttura di qualcun altro |
| Costo di setup | Installazione pacchetto | Immagine da 4,3 GB + container + token | API key |
| Timing misurato qui | Non misurato in questo articolo | 0,32 s al primo render dopo readiness HTTP; chiamate successive 0,15 s mediane | Non misurato in questo articolo |
| Cosa scrivi | Codice di automazione con attese esplicite | Un singolo POST autenticato | Una chiamata HTTP |
| Cosa torna indietro | Ciò che scripti | HTML, risposte PNG/PDF con verifica della firma, nodi estratti | JSON strutturato o Markdown specifici del prodotto |
| Limite di capacità | La tua macchina | CONCURRENT + QUEUED, poi 429 | Il piano del provider |
| Chi è reperibile | Tu | Tu | Loro |
Se vuoi un browser nel tuo processo e non ti dispiace scrivere le attese, una libreria è più leggera e non richiede deployment. Ho approfondito quel lato nel confronto Playwright vs Puppeteer e nella panoramica più ampia sui migliori progetti open source per lo scraping.
Se non vuoi gestire affatto un browser, il nostro Thunderbit è un’alternativa gestita. Browserless restituisce materiale renderizzato che il tuo codice interpreta; Thunderbit può restituire Markdown o dati allineati a uno schema, mentre il provider si occupa dell’infrastruttura di rendering. Questo articolo non ha benchmarkato latenza, capacità, comportamento in caso di errore, qualità dell’estrazione o costo di Thunderbit, quindi la tabella descrive i confini di responsabilità e non un confronto prestazionale.
Letture correlate dello stesso giro di test: la recensione di Crawl4AI copre una pipeline Markdown basata su browser che gestisci tu, e la panoramica sugli strumenti di web scraping mappa la categoria più ampia.
Prova Thunderbit per l’estrazione di dati dal web
Verdetto
Conviene usare Browserless? Sì, se più chiamanti hanno bisogno di lavoro browser, qualcuno può operare il container e la revisione di licenza approva il modello di deployment. In tre run sintetiche di ammissione, il numero di richieste accettate ha corrisposto a CONCURRENT + QUEUED, l’eccedenza ha ricevuto 429 e /pressure ha coinciso con i conteggi visibili dal client. In un controllo sequenziale separato da 30 sessioni, non si è accumulato alcun processo Chrome o zombie. Un trial di timeout ha restituito 408 vicino al limite configurato. Sono osservazioni utili e circoscritte, non garanzie universali.
Misura però l’impegno in modo onesto. È un’immagine da 4,34 GB e un servizio che devi gestire, non una dipendenza da aggiungere; questo test non ha stabilito un confronto di velocità con una libreria browser in-process. Il vantaggio è un browser che puoi razionare: un tetto noto e una backpressure misurabile. Nel controllo sequenziale da 30 sessioni non sono stati osservati processi Chrome o zombie in accumulo. I costi sono peso di deployment e una licenza da leggere. Se devi renderizzare poche pagine da uno script singolo, questo compromesso non conviene. Se stai gestendo un livello di rendering da cui dipendono più servizi, sì — basta però controllare le variabili d’ambiente v1 all’ingresso, perché PREBOOT starà lì a sembrare operativo mentre non fa assolutamente nulla.
Prova Thunderbit per l’estrazione di dati dal web Get Started Free
FAQ
Browserless rende Chrome headless più veloce?
Questo test non può rispondere. L’endpoint HTTP è diventato reattivo 0,78 s dopo docker run; la prima chiamata /content ha poi richiesto 0,32 s e le successive nello stesso container circa 0,15 s. Questi numeri combinano round trip HTTP, lavoro browser, navigazione e trasferimento della risposta. L’harness non ha isolato il tempo di avvio, non ha tracciato il riuso dei processi e non ha pubblicato un benchmark comparabile in-process. Usa Browserless per un confine di servizio condiviso e controllo dell’ammissione, poi misura la tua latenza reale.
Cosa succede quando superi il limite di concorrenza di Browserless?
Ricevi subito un HTTP 429. Il tetto è esattamente CONCURRENT + QUEUED, e l’ho confermato in tre configurazioni: (2,2) ha accettato 4 e rifiutato 4, (3,5) ha accettato 8 e rifiutato 4, (5,5) ha accettato 10 e rifiutato 4. L’endpoint /pressure del server ha riportato ogni volta conteggi corrispondenti di running, queued e recentlyRejected. Da sapere: le richieste in coda non falliscono, aspettano — con (2,2) e lavoro da 5 secondi, le risposte riuscite hanno impiegato tra 5,7 s e 11,0 s. Fai in modo che il client tratti il 429 come backpressure con retry e backoff.
PREBOOT funziona ancora in Browserless v2?
No. PREBOOT è stato rimosso nella 2.0.0 e v2 accetta -e PREBOOT=true senza errori o warning, ma senza farne nulla. Ho confermato l’inerzia in tre modi: la latenza era indistinguibile dal default, un container PREBOOT=true inattivo aveva 0 processi chrome in attesa e /config non espone affatto una chiave preboot. Se hai migrato una configurazione v1, le istanze non sono pre-warmate. Nota che KEEP_ALIVE, rimosso nella stessa release, registra invece un warning "deprecated and ignored" — quindi il problema del fallimento silenzioso è specifico di PREBOOT.
Browserless è gratuito per uso commerciale? Il repository offre SSPL-1.0 o una licenza commerciale Browserless, ma questo articolo non mappa scenari commerciali o closed source specifici a una delle due opzioni. Esamina l’attuale LICENSE e la guida ufficiale al deployment, poi fai valutare il tuo modello di deploy e distribuzione da chi si occupa di licensing software.
Browserless lascia in giro processi Chrome zombie?
Nel breve intervallo testato non ne sono stati osservati in accumulo. Dopo 30 sessioni sequenziali il container aveva 0 processi Chrome e 0 zombie, con soli dumb-init, node, Xvfb, start.sh e sh ancora attivi. Il rilevatore ha contato 11 processi della famiglia Chrome mentre una sessione era viva, quindi non era cieco. La memoria del container è passata da 294 MiB a 303 MiB e poi si è appiattita nei campioni. Questo non è un risultato di durata di ore, concorrenza o migliaia di sessioni.


