Il download del backup non può essere ripreso dopo un'interruzione perché il token email è già stato consumato

Ricordo un problema simile che sembra essere stato risolto in 2026.01.0-latest, quindi credo che si tratti di un problema attuale e distinto, legato al ripristino di un download di backup interrotto, piuttosto che al vecchio errore di download iniziale.

È plausibile pensare che il link di backup a tempo limitato possa impedire al browser di riprendere un download interrotto, ma non è possibile scaricare un backup due volte con un singolo link. Quindi, quando fallisce una volta, la ripresa del download viene probabilmente annullata da Discourse stesso.

Sono ora riuscito a riprodurre esattamente quel comportamento sull’attuale versione di Discourse.

Riproduzione

Stavo scaricando un backup locale di Discourse di:

4.231.639.143 byte

utilizzando Safari su iOS.

Il download stava procedendo normalmente. Ho una registrazione dello schermo che mostra l'aumento da circa:

https://youtube.com/shorts/_Pjc-kxJ1OE?feature=shared

313,3 MB a 317,8 MB

Ho poi lasciato Safari per circa 15 secondi.

Quando sono tornato su Safari, il download si era fermato a circa:

319,8 MB

e Safari mostrava il controllo per il nuovo tentativo.


Il log di accesso di Caddy per la richiesta originale mostra:

GET /admin/backups/...

HTTP/3

Range: none

status: 200

Content-Length: 4231639143

size: 319864024

Ho poi utilizzato il controllo di nuovo tentativo di Safari stesso.

Safari ha effettuato una vera richiesta HTTP byte-range:

GET /admin/backups/...

HTTP/3

Range: bytes=319799904-

status: 422

Quindi Safari non stava semplicemente tentando un altro download completo. Stava correttamente cercando di riprendere il file esistente all’incirca nel punto in cui la precedente trasmissione si era interrotta.

Discourse ha rifiutato quella richiesta Range con 422.

Codice attuale di Discourse

Guardando l’attuale Admin::BackupsController#show, la sequenza è efficacemente:

EmailBackupToken.compare(current_user.id, token)

        ↓

find backup

        ↓

EmailBackupToken.del(current_user.id)

        ↓

send_file

Quindi la richiesta autenticata iniziale consuma il token email prima che la trasmissione di diversi gigabyte sia effettivamente completata.

Se quella trasmissione viene successivamente interrotta, Safari effettua un’altra richiesta come:

Range: bytes=319799904-

ma quella richiesta passa di nuovo attraverso /admin/backups/… e il token email è già stato eliminato.

Sembra che questo spieghi il 422 che ho catturato.

La sessione autenticata è ancora richiesta oltre al token di backup, quindi non sto suggerendo di rendere semplicemente riutilizzabile indefinitamente il token esistente.

Piuttosto, mi sto chiedendo se ci dovrebbe essere un modo limitato per lo stesso amministratore autenticato di riprendere lo stesso download di backup già autorizzato.

Ad esempio, potrebbe essere un’autorizzazione di download a breve durata associata allo stesso utente e backup, anche se i manutentori potrebbero avere un modo migliore per gestire questo.

Perché questo sta diventando più facile da riprodurre su iOS

L’interruzione che ha esposto questo problema sembra anche simile ai resoconti degli utenti di versioni recenti di iOS.

Ci sono almeno tre resoconti ospitati su Apple Community:

resoconti più vecchi

“Safari Background Downloads Failing After IOS 26 update” — Ottobre 2025

Safari Background Downloads Failing After… - Apple Community

L’utente riporta che i download più grandi di Safari falliscono quando vengono eseguiti in background dopo l’aggiornamento a iOS 26. Hanno anche aggiornato un iPhone 15 Pro a iOS 26 e riportato lo stesso comportamento.

“Safari iOS Background Download Issue - Cancels on Minimize” — Dicembre 2025

Safari iOS Background Download Issue - Ca… - Apple Community

Riportato su un iPhone 15 con iOS 26.1. I download iniziano normalmente ma vengono annullati dopo il passaggio a un’altra app o la minimizzazione di Safari.

“Safari can’t complete a large download” — Aprile 2026

Riportato su un iPhone 17 Pro Max con iOS 26.4. I download di grandi dimensioni si fermano brevemente dopo aver bloccato il dispositivo, lasciando Safari, o passando tra cellulare e Wi-Fi. L’autore riporta specificamente un buon segnale e dice che gli stessi download si completano su PC, Mac e Android.

Non credo che quei resoconti della comunità siano sufficienti per dire che Apple ha confermato una regressione di iOS.

Tuttavia, sono coerenti con la mia registrazione dello schermo: il backup stava scaricando normalmente, sono passato brevemente da Safari e quando sono tornato la trasmissione era diventata interrotta.

Il punto importante sul lato Discourse è indipendente dalla causa dell’interruzione:

la trasmissione del backup di grandi dimensioni è interrotta

        ↓

il browser tenta una ripresa Range basata sugli standard

        ↓

il token di backup è già stato consumato

        ↓

la ripresa riceve 422

Osservazioni lato server

Questo non sembra essere un problema generale con l’attuale percorso nginx/sendfile.

Lo stesso percorso di backup multi-GB ha completato con successo:

  • da Firefox/Linux tramite HTTP/3; e
  • dallo stesso iPhone tramite Wi-Fi usando HTTP/3.

L’attuale nginx pubblicizza anche il supporto per byte-range.

Quindi non sto suggerendo che HTTP/3, Caddy o nginx siano intrinsecamente incapaci di fornire il backup.

In questa particolare riproduzione, la trasmissione originale è stata interrotta dopo circa 320 MB, Safari ha successivamente effettuato una richiesta Range apparentemente valida, e quella seconda richiesta è stata rifiutata da Discourse.

Avrebbe senso che un download di backup già autorizzato avesse un meccanismo a breve durata che consenta allo stesso amministratore autenticato di riprendere quello stesso file dopo una trasmissione interrotta?

I had a look through the history and current test coverage to get a better idea of what a possible fix would need to preserve.

The one-use behaviour dates back to the March 2017 security-hardening change:

That commit introduced emailed backup-download tokens and also consumed the token immediately once the download request had been authorized.

The same ordering still exists in current Admin::BackupsController#show:

The underlying token implementation is also still very simple:

EmailBackupToken stores one Redis token per user with a one-day expiry. It is user-scoped rather than being tied to a particular backup.

Looking through that file’s history, the changes since 2017 appear to have been mechanical/style changes rather than changes to the authorization model.

That makes simply leaving the existing emailed token reusable seem undesirable: it would weaken the original one-use restriction, and the token itself is not scoped to the particular backup in the URL.

The current request tests are here:

They cover an initial valid download, invalid tokens, invalid/missing backup IDs and permissions, but I couldn’t find coverage for an interrupted transfer followed by a Range: request.

The API request spec likewise documents the ordinary filename + token download path:

A possible patch direction therefore seems to be to preserve the existing one-use email token for initiating the backup download, while providing a separate short-lived resume authorization for the already-authorized local backup.

I would expect such a resume authorization to be narrowly scoped to the authenticated user and specific backup, and to permit a subsequent byte-range request without making the original one-day user-wide email token generally reusable.

The behaviours I’d aim to preserve/add are:

  • initial GET with a valid emailed token remains allowed;

  • another ordinary full GET using the consumed email token remains rejected;

  • a byte-range retry for the same backup by the same authenticated admin can be resumed for a short bounded period;

  • a resume attempt against another backup remains rejected;

  • an expired resume authorization remains rejected.

I haven’t assumed that this is necessarily the implementation maintainers will prefer, but the code/test surface appears fairly contained.

I’m happy to put together a draft patch and regression tests for the Safari Range: reproduction if this general security model sounds reasonable.