Un caricamento che fallisce a una soglia di dimensione è un limite: vedi 413 Richiesta troppo grande. Un caricamento che fallisce a metà, a dimensioni sempre diverse, con la connessione che semplicemente cade, è un tempo massimo da qualche parte lungo il percorso.

I tempi massimi da alzare, tutti quanti

# nginx
client_body_timeout 300s;
client_max_body_size 512M;
send_timeout 300s;
fastcgi_read_timeout 300s;
proxy_read_timeout 300s;
; php
max_execution_time = 300
max_input_time = 300      ; the one that is forgotten - it covers the UPLOAD itself

max_input_time limita quanto tempo PHP dedicherà a ricevere la richiesta. È separato da max_execution_time, ha un valore predefinito basso, ed è ciò che uccide un caricamento lento fatto da un telefono.

Disco e spazio temporaneo

df -h /tmp /var/lib/php/tmp
grep upload_tmp_dir /etc/php/8.3/fpm/php.ini

Un caricamento viene prima scritto in un file temporaneo. Se quel filesystem si riempie, la scrittura fallisce a metà trasferimento e la connessione cade senza lasciare un messaggio da nessuna parte.

Cerca cosa è stato registrato

sudo tail -50 /var/log/nginx/error.log
sudo journalctl -u php8.3-fpm -n 50 --no-pager

client intended to send too large body è il limite di dimensione. timed out e upstream prematurely closed sono i tempi massimi qui sopra. Nessuna traccia significa di solito che il browser o un proxy ha ceduto prima del server.

Dietro Cloudflare il proxy ha un proprio tetto di caricamento nei piani più bassi, e si applica qualunque cosa tu imposti in locale. Carica su un sottodominio che aggira il proxy, oppure carica a pezzi.

La soluzione giusta per i file grandi

Il caricamento a pezzi o riprendibile manda un file grande come tante richieste piccole: così nessuna singola richiesta dura a lungo e una connessione caduta riprende invece di ricominciare. Per qualsiasi cosa oltre qualche centinaio di megabyte è l'unica risposta affidabile.