GitHub Pages es una de las opciones más populares para alojar sitios estáticos de forma gratuita. Lo puedes usar para documentación, portfolios, landing pages e incluso para aplicaciones web completas generadas con frameworks como Astro, Next.js o Hugo.
Últimamente, además, estoy haciendo bastante uso de GitHub Pages para side projects o demos en las que simplemente necesito servir contenido estático sin complicarme demasiado. Y es que ya te conté en otro artículo que incluso puedes tener repositorios privados que desplieguen en GitHub Pages y que ese contenido sea público… algo que, sinceramente, me está resultando extremadamente útil en mi día a día.
Pero claro… aquí viene la parte que no deberíamos pasar por alto 👀
Porque aunque sea “solo estático”, seguimos exponiendo una aplicación web al mundo. Y eso implica que la seguridad sigue siendo importante.
En este artículo te voy a contar qué puedes (y qué no puedes) hacer para mejorar la seguridad de un sitio alojado en GitHub Pages, centrándome en una de las herramientas más potentes que tenemos a nuestro alcance: Content Security Policy (CSP).
¿Qué es GitHub Pages y cuáles son sus limitaciones?
GitHub Pages es un servicio de hosting estático que sirve archivos HTML, CSS y JavaScript directamente desde un repositorio de GitHub. No hay servidor backend, no hay base de datos, no hay procesamiento del lado del servidor. Solo archivos estáticos.
Esto tiene muchas ventajas (rapidez, simplicidad, coste cero), pero también una limitación importante para la seguridad: no puedes configurar headers HTTP personalizados. Y los headers HTTP son el mecanismo principal para aplicar políticas de seguridad en la web.
Dicho esto, GitHub Pages sí aplica algunas protecciones por defecto:
- HTTPS forzado con
Strict-Transport-Security - Certificados TLS gestionados automáticamente
- Infraestructura de CDN global
Pero hay muchas cosas que no puedes controlar: X-Frame-Options, Permissions-Policy, o el propio Content-Security-Policy como header. Para eso tenemos un plan B.
¿Qué es Content Security Policy (CSP)?
CSP es un estándar de seguridad web que permite definir qué recursos puede cargar tu página. Funciona como una lista blanca: solo se ejecutan scripts, se cargan estilos, fuentes o se hacen peticiones de red a los orígenes que tú autorices explícitamente.
La forma habitual de aplicar CSP es mediante un header HTTP:
Content-Security-Policy: default-src 'self'; script-src 'self';Pero como en GitHub Pages no podemos configurar headers, usamos la alternativa: una etiqueta <meta> en el <head> del HTML:
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self' 'unsafe-inline';"
/>El navegador lee esta etiqueta y aplica las mismas restricciones que si viniera como header. No importa si tu hosting es GitHub Pages o cualquier otro. Es el navegador quien lo interpreta.
¿De qué te protege CSP?
El principal ataque que mitiga CSP es Cross-Site Scripting (XSS). Si un atacante consigue inyectar un <script src="https://malicious.com/steal.js"> en tu página, el navegador lo bloqueará porque malicious.com no está en tu lista de orígenes permitidos.
Además, CSP ayuda contra:
- Exfiltración de datos: un script malicioso no puede enviar datos a servidores no autorizados si
connect-srcestá restringido. - Inyección de estilos: con
style-srcpuedes evitar que se carguen CSS externos maliciosos. - Carga de recursos no deseados: controlas de dónde vienen las imágenes (
img-src), las fuentes (font-src) y todo lo demás.
Ejemplo práctico: un sitio Astro en GitHub Pages
Recientemente construí una aplicación de encuestas anónimas hecha con Astro + React, desplegada en GitHub Pages y estas son las decisiones de seguridad que tomé:
La política CSP que apliqué
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self';
script-src 'self' 'unsafe-inline';
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
font-src https://fonts.gstatic.com;
img-src 'self' data:;
connect-src 'self' https://api.web3forms.com https://api.fpjs.io;
base-uri 'self';
form-action 'none';"
/>Si echamos un vistazo a cada una de las directivas:
| Directiva | Qué permite | Por qué |
|---|---|---|
default-src 'self' | Solo recursos del propio dominio | Política restrictiva por defecto |
script-src 'self' 'unsafe-inline' | Scripts propios + inline | Astro/React necesitan inline scripts para la hidratación |
style-src 'self' 'unsafe-inline' fonts.googleapis.com | Estilos propios, inline y Google Fonts | Tailwind genera estilos inline; usamos Google Fonts |
font-src fonts.gstatic.com | Fuentes de Google | Las fuentes se sirven desde gstatic |
img-src 'self' data: | Imágenes propias y data URIs | Los SVG inline usan data: |
connect-src 'self' api.web3forms.com api.fpjs.io | Fetch/XHR a servicios autorizados | Web3Forms para emails, FingerprintJS para deduplicación |
base-uri 'self' | Solo base URI propia | Evita ataques que cambien el <base> del HTML |
form-action 'none' | Bloquea envío de forms HTML | Usamos fetch(), no formularios nativos |
Otras meta etiquetas de seguridad
Además del CSP, añadí estas dos:
<!-- Evita enviar el referrer a sitios externos -->
<meta name="referrer" content="no-referrer" />
<!-- Evita que el navegador adivine el MIME type -->
<meta http-equiv="X-Content-Type-Options" content="nosniff" />Cómo probarlo
Para comprobar que esto que hemos configurado tiene su efecto puedes abrir las DevTools de tu navegador y en la sección de consola podrías intentar añadir algo como esto:
const s = document.createElement('script');
s.src = 'https://evil.example.com/test.js';
document.head.appendChild(s);Si está bien configurado debería de devolver algo como lo siguiente:
Deberías ver un error de CSP en la consola confirmando que script-src bloquea el recurso. Si no aparece error, la política no se está aplicando.
También es posible confirmar la configuración aplicada yendo a la sección Application > Frames > top y ahí podrás ver una sección sobre la configuración relacionada con Content Security Policy (CSP)
Lo que NO puedes hacer con meta tags
Hay directivas CSP y headers de seguridad que solo funcionan como headers HTTP y no se pueden aplicar via <meta>:
frame-ancestors: controla quién puede embeber tu página en un iframe. En GitHub Pages no puedes configurarlo.Permissions-Policy: restringe el acceso a APIs del navegador (cámara, micrófono, geolocalización). No existe como meta tag.X-Frame-Options: la versión legacy deframe-ancestors. Tampoco funciona como meta tag.
Si necesitas control total sobre estos headers, tendrás que considerar un hosting que permita configurar headers HTTP personalizados.
Conclusión
GitHub Pages es una opción más que suficiente para sitios estáticos, y aunque tiene limitaciones al no poder configurar headers HTTP, puedes hacer bastante con meta tags. Un CSP bien configurado via <meta> te protege contra XSS, controla qué recursos se cargan y limita las conexiones salientes.
No esperes a tener un hosting «de pago» para aplicar buenas prácticas de seguridad. Con unas pocas líneas en tu <head> puedes mejorar significativamente la postura de seguridad de tu sitio.
¡Nos vemos 👋🏻!


