
Cómo Solucionar el Bloqueo de Imágenes HTTP en Sitios HTTPS mediante un Proxy en Symfony
El Problema: Contenido Mixto (Mixed Content)
Al integrar recursos externos (como imágenes) en un sitio HTTPS, los navegadores bloquean aquellos servidos mediante HTTP por razones de seguridad. Este escenario es común al crear lectores RSS que consumen fuentes con enlaces HTTP, resultando en imágenes rotas
La Solución: Un Proxy de Imágenes en el Backend
La estrategia consiste en crear un proxy que descargue las imágenes desde HTTP y las sirva a través de HTTPS, evitando el bloqueo. A continuación, detallamos una implementación en Symfony.
Paso 1: Crear el Controlador Proxy
Al integrar recursos externos (como imágenes) en un sitio HTTPS, los navegadores bloquean aquellos servidos mediante HTTP por razones de seguridad. Este escenario es común al crear lectores RSS que consumen fuentes con enlaces HTTP, resultando en imágenes rotas
La Solución: Un Proxy de Imágenes en el Backend
La estrategia consiste en crear un proxy que descargue las imágenes desde HTTP y las sirva a través de HTTPS, evitando el bloqueo. A continuación, detallamos una implementación en Symfony.
Paso 1: Crear el Controlador Proxy
#[Route(path: '/images/{url}', name: 'image_proxy', methods: ['GET'])] public function proxyImage( Request $request, string $url, CacheInterface $cache // Inyectar servicio de caché ): StreamedResponse|RedirectResponse { $targetUrl = urldecode(base64_decode($url)); // Validaciones de Seguridad if (!$this->isValidUrl($targetUrl) || !$this->isUrlAllowed($targetUrl)) { throw $this->createNotFoundException('URL no permitida o inválida'); } // Redirigir directamente si es HTTPS if (str_starts_with($targetUrl, 'https')) { return new RedirectResponse($targetUrl); } // Usar caché para evitar descargas repetidas $cacheKey = md5($targetUrl); if ($cachedImage = $cache->get($cacheKey)) { return new Response($cachedImage, 200, ['Content-Type' => 'image/jpeg']); } try { $response = $this->httpClient->request('GET', $targetUrl, [ 'headers' => $this->getBrowserLikeHeaders() ]); $content = $response->getContent(); $cache->set($cacheKey, $content, 86400); // Cachear por 24h return new StreamedResponse( function () use ($response) { foreach ($this->httpClient->stream($response) as $chunk) { echo $chunk->getContent(); flush(); } }, $response->getStatusCode(), [ 'Content-Type' => $response->getHeaders()['content-type'][0] ?? 'application/octet-stream', 'Cache-Control' => 'public, max-age=86400' ] ); } catch (\Exception $e) { // Opcional: Registrar el error en logs return new RedirectResponse('https://titulares.mrk-cu.com/text_titulares.png'); } } // Validar formato de URL private function isValidUrl(string $url): bool { return filter_var($url, FILTER_VALIDATE_URL) !== false; } // Restringir dominios permitidos (ajustar según necesidades) private function isUrlAllowed(string $url): bool { $allowedHosts = ['.cu', 'dominio-permitido.com']; $host = parse_url($url, PHP_URL_HOST); foreach ($allowedHosts as $allowed) { if (str_ends_with($host, $allowed)) return true; } return false; } // Headers para evitar bloqueos private function getBrowserLikeHeaders(): array { return [ 'User-Agent' => 'Mozilla/5.0 (X11; Linux x86_64)...', 'Accept' => 'image/webp,image/apng,image/*,*/*;q=0.8', 'Referer' => 'https://google.com' ]; }
Paso 2: Integrar el Proxy en Twig
<img src="{{ image_url starts with 'http://' ? path('image_proxy', {url: image_url|url_encode|base64_encode}) : image_url }}" alt="Imagen" >
Requiere un filtro personalizado para base64_encode.
Mejoras Clave Implementadas
- Seguridad Reforzada
- Validación estricta de URLs con filter_var().
- Lista blanca de dominios permitidos (evita SSRF).
- Optimización con Caché
- Almacena imágenes temporalmente para reducir peticiones al servidor externo y mejorar rendimiento.
- Manejo de Errores
- Redirige a una imagen por defecto en fallos, evitando rupturas en la UI.
Consideraciones Finales
- Ajusta la Caché: Modifica max-age según la frecuencia de actualización de las imágenes.
- Monitorización: Implementa logs para rastrear errores o intentos de acceso a URLs no permitidas.
- Escalabilidad: Para alto tráfico, considera usar un CDN o almacenamiento en cloud para las imágenes cacheadas.
Con este enfoque, resolverás el problema de contenido mixto manteniendo la seguridad y eficiencia de tu aplicación.
Deja un comentario: