Publicar el cliente y que la descarga se pueda reanudar
El cliente de Ragnarok pesa cientos de megas, a veces más de un giga. Publicarlo es la parte fácil. La parte que nadie mira hasta que se queja un jugador es qué pasa cuando la conexión se corta al 90 %: si la descarga no se puede reanudar, empieza de cero.
1 · Los tres orígenes #
En Admin → Descargas, cada archivo se publica desde uno de tres orígenes:
- URL externa — el archivo está en otro sitio: Google Drive, MediaFire, o un subdominio tuyo. El panel redirige al jugador.
- Subir archivo — para archivos chicos, dentro del límite de subida de tu PHP (el formulario te dice cuál es).
- Local (FTP) — para archivos grandes: los dejas por FTP en una carpeta del servidor y el panel los detecta solo.
Existe además la casilla Ocultar URL externa, que reenvía el archivo a través de tu panel para que el jugador no vea la dirección real. Funciona en streaming y respeta la reanudación, pero todo el tráfico atraviesa tu PHP. Para un cliente de 1 GB conviene más la URL externa normal.
2 · Publicar el cliente por FTP #
El upload_max_filesize habitual de PHP anda entre 2 y 20 MB, muy por
debajo de lo que pesa un cliente. Por eso no se sube por el navegador: se deja por FTP
o SFTP en esta carpeta de tu servidor.
storage/app/public/downloads/client/
Después, en el panel:
- Sube el archivo por FTP/SFTP a esa carpeta.
- En Admin → Descargas → Nueva descarga, elige la pestaña Local (FTP).
- El archivo aparece en la lista con su tamaño — selecciónalo y guarda.
La pestaña muestra la ruta absoluta de tu servidor, así que no hace falta recordarla. Si la carpeta no existe, el panel la crea al abrir esa pestaña.
Permisos. El archivo tiene que quedar legible por el usuario de PHP
(normalmente www-data). Si lo subes con otro usuario y la descarga da
error, corrígelo con
chown www-data:www-data storage/app/public/downloads/client/*.
Hace falta el enlace de storage. El instalador ya ejecuta
php artisan storage:link. Si la descarga devuelve 404, comprueba que
exista el enlace public/storage.
3 · Que la descarga se pueda reanudar #
Un jugador con una conexión inestable que pierde la señal al 90 % de un giga debería
poder continuar donde quedó. Para eso existen las peticiones de rango
(la cabecera Range), y tu panel ya hace su parte: sirve
los archivos con una respuesta que atiende el rango y contesta
206 Partial Content sin que tengas que configurar nada.
Lo que rompe la reanudación está delante del panel. Un CDN deja de
reenviar el Range a tu servidor cuando intenta cachear un archivo
que supera su máximo cacheable — en los planes no Enterprise de Cloudflare son 512 MB,
justo por debajo del tamaño de un cliente de Ragnarok. Y el síntoma engaña: el CDN
sigue anunciando accept-ranges: bytes, así que el gestor de descargas del
jugador cree que puede reanudar, y no puede.
Peor todavía: es intermitente, porque depende del estado de la caché para ese archivo concreto. Medido sobre el mismo archivo de 998 MB:
cf-cache-status: MISS— el CDN está intentando cachearlo: responde 200 y empieza a mandar el archivo entero. Roto.cf-cache-status: BYPASS— ya sabe que no puede cachearlo: responde 206. Correcto.cf-cache-status: HIT— archivo chico, cacheado: responde 206. Correcto.
El panel lo mide por ti. Admin → Health Check →
Descargas reanudables toma tu descarga más grande — local, o publicada como
URL externa apuntando a tu propio dominio — pide un trozo y comprueba si vuelve un
206 de verdad. Como el resultado depende del estado de la caché, un
«ok» describe el momento en que se midió.
4 · Las tres salidas #
De mejor a peor, si el Health Check te avisa:
-
Servir esos archivos fuera del CDN. Un subdominio en modo
DNS-only (la nube gris de Cloudflare), por ejemplo
descargas.tuservidor.cl, apuntando a la misma carpeta; en el panel se publica con el origen URL externa. Recuperas la reanudación y de paso el tráfico pesado deja de pasar por el CDN. Lee antes el aviso de aquí abajo: tiene un costo en seguridad. -
Desactivar la caché para esa ruta. Una regla del CDN
(Cache Rule → bypass) sobre
/storage/downloads/*y/downloads/*. Es lo que estabiliza el206según la lista de arriba, y es la salida más rápida si no quieres tocar el DNS. - Partir el instalador en varias partes de 200 a 300 MB. No arregla nada, pero limita lo que se pierde en cada corte.
La primera salida tiene un costo: publica la IP de tu servidor. Un
subdominio en DNS-only resuelve directo a tu origen, así que ese servidor deja de
estar escondido detrás del CDN. Quien mire ese registro DNS puede conectarse a tu
máquina saltándose el WAF, los límites de peticiones y la protección contra
denegación de servicio — y no solo para las descargas: con esa IP también puede
llegar a tu panel directamente, indicando el dominio principal en la cabecera
Host.
Suplantar la dirección de otra persona no funciona, eso sí: rApanel solo cree en las
cabeceras X-Forwarded-For que vienen de tu propio servidor, nunca en las
que manda un visitante. Pero el origen queda alcanzable, y esa es la parte que no se
deshace.
Si prefieres no exponerlo, quédate con la salida 2: el bypass de caché mantiene todo detrás del CDN. Y si aun así vas por el subdominio, lo ideal es que apunte a otro servidor, no al que sirve el panel.
5 · Comprobarlo tú mismo #
Pide un trozo de mil bytes y mira qué contesta:
# A través del CDN
curl -s -o /dev/null -r 1000000-1001023 \
-w 'HTTP %{http_code} · %{size_download} bytes\n' \
https://tuservidor.cl/downloads/mi-slug/get
# Directo a tu servidor, sin pasar por el CDN
curl -s -o /dev/null -k --resolve tuservidor.cl:443:127.0.0.1 \
-r 1000000-1001023 -w 'HTTP %{http_code} · %{size_download} bytes\n' \
https://tuservidor.cl/downloads/mi-slug/get
HTTP 206 · 1024 bytes es lo correcto. Si sale 200 y una
cantidad de bytes que crece, el CDN está ignorando el rango y mandando el archivo
completo — que es exactamente lo que le pasa al jugador cuando intenta reanudar.
Ojo con curl -I. Una petición HEAD
no sirve para probar esto: devuelve 200 y el
Content-Length del archivo entero incluso cuando el rango funciona
perfectamente. Es una forma muy fácil de diagnosticar un problema que no existe —
o de no ver el que sí tienes. Mídelo con un GET real, como arriba.