Si el chatbot de MuseRelay no funciona bien en tu web y ves mensajes de "Refused to connect" en la consola del navegador, casi seguro es un tema de CSP (Content Security Policy). Aquí te explicamos qué es, cómo detectarlo y cómo arreglarlo en minutos.
Cómo saber si tu web tiene este problema
- Abre tu web donde está embebido el chatbot de MuseRelay.
- Pulsa F12 (abre las DevTools del navegador).
- Ve a la pestaña Console.
- Envía un mensaje desde el chatbot.
- Si ves mensajes parecidos a estos, tienes un problema de CSP:
Refused to connect to 'wss://muserelay.com/ws' because it violates
the following Content Security Policy directive: connect-src 'self' ...
Refused to load the script 'https://muserelay.com/bot/123/script.js'
because it violates the following Content Security Policy directive: script-src 'self' ...
¿Qué es CSP y por qué me afecta?
Content Security Policy es una cabecera HTTP que tu servidor envía al navegador diciéndole: "solo puedes conectar con estos dominios, solo puedes ejecutar scripts desde estos otros". Es una medida de seguridad buena — evita que un atacante inyecte código malicioso.
El problema es cuando tu CSP es demasiado restrictiva y bloquea también recursos legítimos como el chatbot de MuseRelay. La solución no es quitar el CSP, sino añadir una excepción para nuestros dominios.
Qué tienes que permitir para MuseRelay
MuseRelay necesita que tu CSP permita acceso a estos dominios y tipos de recurso:
| Directiva CSP | Dominios a añadir | Para qué |
|---|---|---|
script-src |
https://muserelay.com |
Cargar el JavaScript del widget |
connect-src |
https://muserelay.com wss://muserelay.com |
WebSocket en tiempo real + peticiones HTTP de fallback |
img-src |
https://muserelay.com data: |
Logo del bot y avatares |
style-src |
https://muserelay.com 'unsafe-inline' |
Estilos del widget (necesita inline para temas) |
️ El crítico es wss://muserelay.com en connect-src — sin eso, el chatbot cae a polling HTTP y pierdes la experiencia en tiempo real (streaming token a token, respuestas instantáneas, etc.).
️ Cómo añadirlo según tu plataforma
Nginx
Edita el server block de tu dominio:
add_header Content-Security-Policy "default-src 'self'; \
script-src 'self' https://muserelay.com 'unsafe-inline'; \
connect-src 'self' https://muserelay.com wss://muserelay.com; \
img-src 'self' https://muserelay.com data:; \
style-src 'self' https://muserelay.com 'unsafe-inline';" always;
Después sudo nginx -t && sudo systemctl reload nginx.
Apache (.htaccess)
<IfModule mod_headers.c>
Header set Content-Security-Policy "default-src 'self'; \
script-src 'self' https://muserelay.com 'unsafe-inline'; \
connect-src 'self' https://muserelay.com wss://muserelay.com; \
img-src 'self' https://muserelay.com data:; \
style-src 'self' https://muserelay.com 'unsafe-inline';"
</IfModule>
Caddy
tusitio.com {
header Content-Security-Policy "default-src 'self'; \
script-src 'self' https://muserelay.com 'unsafe-inline'; \
connect-src 'self' https://muserelay.com wss://muserelay.com; \
img-src 'self' https://muserelay.com data:; \
style-src 'self' https://muserelay.com 'unsafe-inline';"
}
Después sudo systemctl reload caddy.
Meta tag HTML (cualquier CMS)
Si no tienes acceso al servidor, puedes ponerlo en el <head> de tu HTML:
<meta http-equiv="Content-Security-Policy" content="
default-src 'self';
script-src 'self' https://muserelay.com 'unsafe-inline';
connect-src 'self' https://muserelay.com wss://muserelay.com;
img-src 'self' https://muserelay.com data:;
style-src 'self' https://muserelay.com 'unsafe-inline';
">
frame-ancestors ni report-uri. Para 90% de los casos va bien.
Cloudflare (Transform Rules)
- Panel Cloudflare → Rules → Transform Rules → Modify Response Header.
- Crear una regla para tu dominio.
- Set dynamic → header
Content-Security-Policy→ pegar el valor completo. - Deploy.
WordPress
Lo más sencillo: plugin HTTP Headers o Really Simple SSL Pro. Permiten editar CSP desde el admin.
Alternativa manual: en el functions.php de tu tema (o mejor, en un child theme):
add_action('send_headers', function() {
header("Content-Security-Policy: default-src 'self'; " .
"script-src 'self' https://muserelay.com 'unsafe-inline'; " .
"connect-src 'self' https://muserelay.com wss://muserelay.com; " .
"img-src 'self' https://muserelay.com data:; " .
"style-src 'self' https://muserelay.com 'unsafe-inline';");
});
Cómo comprobar que funciona
- Recarga tu web con Ctrl+Shift+R (fuerza refresco de caché).
- Abre la consola del navegador (F12).
- Envía un mensaje desde el chatbot.
- Ya NO deberías ver el error "Refused to connect".
- En la pestaña Network busca
ws— debería aparecer una conexión WebSocket activa (Status: 101 Switching Protocols).
Si ves la conexión WebSocket y el mensaje aparece palabra por palabra en el chat, ¡listo! El streaming en tiempo real está funcionando.
Si tu proveedor de hosting no te deja editar CSP
Algunos hostings (WordPress.com, Wix, Shopify básico, plataformas SaaS tipo Musedock) no te permiten modificar las cabeceras HTTP. En ese caso tienes dos opciones:
- Meta tag HTML (ver arriba). Función casi siempre.
- Pedir a tu proveedor que permita overrides de CSP por dominio. Copia-pega el texto de abajo en su soporte — está redactado para devs y les da toda la info técnica:
Prompt copy-paste para tu proveedor de hosting
Hola. Tengo un problema con CSP en el sitio que tengo alojado con vosotros
y quiero proponer una mejora para poder embeber herramientas externas
(chatbots, widgets de terceros, analytics, etc.).
PROBLEMA:
Vuestra plataforma aplica por defecto cabeceras Content-Security-Policy que
impiden que recursos externos legítimos funcionen. Por ejemplo, estoy embebiendo
el widget de MuseRelay y el WebSocket se bloquea:
Refused to connect to 'wss://muserelay.com/ws' because it violates
the following Content Security Policy directive: connect-src 'self'
El widget cae a polling HTTP (peor UX). Lo correcto sería poder autorizar
WebSocket a muserelay.com.
PROPUESTA:
Añadir en el panel de configuración del sitio campos para permitir overrides
de CSP por dominio:
[1] Dominios permitidos para connect-src (textarea, uno por línea)
Ejemplo: wss://muserelay.com, https://api.stripe.com
[2] Dominios permitidos para script-src
Ejemplo: https://cdn.jsdelivr.net
[3] Dominios permitidos para frame-src / img-src
Comportamiento:
- Vacío → CSP por defecto (actual).
- Con valores → se añaden al CSP existente (no lo reemplazan).
- Preview del CSP final antes de guardar.
Casos de uso que esto resuelve:
- Chatbots/widgets externos (MuseRelay, Intercom, Tidio, Crisp).
- Analytics (Plausible, Fathom, servidores propios).
- Formularios embebidos (Typeform, Calendly).
- Videos (Wistia, servers propios).
- APIs propias del cliente llamadas desde JS frontend.
Implementación sugerida:
1. Columna JSONB `csp_overrides` en la tabla de sitios.
2. Campos en el controller de Settings.
3. Plantilla de vhost (nginx/caddy) lee los arrays y los concatena a las
directivas CSP existentes.
4. Endpoint de preview del CSP final resultante.
5. Validación de esquemas permitidos (https://, wss://, ws:// solo en dev).
Alternativa simple: un único campo "Cabeceras CSP adicionales" como textarea
libre donde el usuario pega directamente directivas. Menos pulido pero funciona.
¿Podéis añadirlo al roadmap? Si ya existe alguna forma actual de customizar
CSP y no la he encontrado, indicadme dónde mirar. Gracias.
Si nada de esto funciona
Mientras resuelves el CSP, el chatbot sigue funcionando — solo pierde el tiempo real. MuseRelay detecta automáticamente si el WebSocket está bloqueado y cae a polling HTTP cada 5 segundos. El usuario recibirá las respuestas del bot, solo que con una pequeña latencia extra.
Si no puedes modificar CSP y quieres forzar el modo polling para que no haya errores en consola, escríbenos a soporte@muserelay.com y te enviamos una variante del script que arranca directamente en polling.
Recursos útiles
- Guía oficial de CSP en MDN
- CSP Evaluator de Google — pegas tu CSP y te dice si tiene problemas.
- securityheaders.com — escanea tu dominio y puntúa todas las cabeceras de seguridad.