Ghost Blog como contenedor: Solucionando el error de “Content Must Be Served Over HTTPS” Mixed Content Error

Ghost Blog como contenedor: Solucionando el error de “Content Must Be Served Over HTTPS” Mixed Content Error
Photo by David Pupăză / Unsplash

Un poco de contexto sobre cómo llegué a este error:

💡
Mi infraestructura está corriendo una versión containerizada de Ghost usando Docker Compose. La aplicación está desplegada detrás de un reverse proxy containerizado con Nginx, que se encarga de la terminación HTTPS utilizando certificados de Let's Encrypt. En este setup, el reverse proxy es responsable de servir el sitio sobre HTTPS, mientras que el contenedor de Ghost se comunica internamente usando HTTP.

Al principio, todo parecía funcionar correctamente. El sitio cargaba sobre HTTPS sin problemas, pero al acceder a ciertas funcionalidades — particularmente el portal de miembros — el navegador comenzó a bloquear solicitudes debido a un error de Mixed Content. La página se estaba sirviendo de forma segura sobre HTTPS, pero Ghost intentaba solicitar recursos internos de la API usando HTTP.

Esto me llevó a inspeccionar la red en Google Chrome, donde apareció el siguiente error:

Mixed Content: The page at 'https://training-stack.com'
was loaded over HTTPS, but requested an insecure resource
'http://training-stack.com/members/api/member/'.
This request has been blocked; the content must be served over HTTPS.

¿Por qué ocurre esto?

Respuesta corta:
Ghost genera las URLs basándose en la URL configurada (variable de entorno) del sitio, no en el proxy.

Respuesta larga:
Esto suele ocurrir cuando Ghost está corriendo detrás de un reverse proxy que maneja SSL (como Nginx o nginx-proxy).
Si la configuración de la URL de Ghost está en http:// en lugar de https://, Ghost generará solicitudes internas a la API usando HTTP, aunque el sitio público esté servido sobre HTTPS.


Solución

Paso 1 — Actualizar la URL de Ghost

Dentro de tu docker-compose, Dockerfile o variables de entorno:

GHOST_URL=https://tudominio.com

Paso 2 — Asegurar que Ghost confíe en el proxy

server__trustProxy: "true"

Paso 3 — Recrear el contenedor

docker compose down
docker compose up -d --force-recreate ghost

Conclusión

Problemas como este son comunes cuando las aplicaciones corren detrás de reverse proxies. La idea clave aquí es que la aplicación debe conocer la URL pública real que están usando los usuarios, incluso si el manejo de SSL ocurre externamente.


Diccionario rápido

Reverse Proxy:
Es un servidor que se sitúa entre el usuario y tu aplicación.

¿Para qué sirve?

  • Permitir múltiples servicios corriendo en un mismo servidor
  • Manejo de HTTPS
  • Seguridad y rate limiting
  • Balanceo de Carga