Publicar una aplicación Blazor es generar los archivos necesarios para ejecutarla en producción, ya sea en un servidor ASP.NET Core o como un sitio estático si usamos WebAssembly standalone.
Hasta ahora hemos ejecutado todo desde Visual Studio o con dotnet run. Eso está muy bien para desarrollar, pero producción es otra película: compilación en Release, archivos optimizados, configuración real, logs, HTTPS y todas esas cosas que no molan tanto hasta que se rompen.
La buena noticia es que Blazor forma parte de ASP.NET Core, así que el flujo base es el mismo que en cualquier aplicación .NET moderna.
Publicar desde la terminal
La forma más directa es usar dotnet publish desde la carpeta del proyecto.
dotnet publish -c ReleaseEsto compila la aplicación, restaura dependencias si hace falta y genera una carpeta publish con los archivos preparados para desplegar.
En proyectos actuales, el resultado suele quedar en una ruta parecida a esta:
bin/Release/net10.0/publishLa versión del framework cambia según el proyecto (net8.0, net9.0, net10.0, etc.). Sin opciones adicionales, la publicación es dependiente del framework: el servidor debe tener instalado el runtime compatible.
Si necesitamos una salida que incluya el runtime, indicamos un identificador de plataforma y publicamos como self-contained:
dotnet publish -c Release -r linux-x64 --self-contained trueEsta salida ocupa más y es específica de la plataforma elegida.
En una aplicación real, lo habitual es que este comando lo ejecute un pipeline de CI/CD. Entender su resultado nos permite reproducir el despliegue y diagnosticarlo.
Publicar desde Visual Studio
Visual Studio también permite publicar desde el asistente de Publish.
Podemos elegir destinos como:
- Carpeta local.
- IIS.
- Azure App Service.
- Azure Static Web Apps.
- Contenedor Docker.
El asistente está bien para empezar, especialmente si desplegamos en Azure. Aun así, conviene saber que se apoya en MSBuild para compilar y generar una salida de publicación.
No todos los Blazor se despliegan igual
Aquí viene la parte que suele confundir al principio. “Blazor” no siempre significa el mismo tipo de despliegue.
Si la aplicación corre en servidor, desplegamos una aplicación ASP.NET Core normal.
El servidor necesita:
- Tener instalado el runtime de .NET correspondiente o recibir una publicación self-contained.
- Ejecutar la aplicación detrás de Kestrel, IIS, Nginx, Apache o Azure App Service.
- Admitir WebSockets y conexiones persistentes si usamos interactividad de servidor.
En este caso, el navegador recibe HTML, CSS, JavaScript y eventos que viajan al servidor cuando el componente es interactivo.
Si distribuimos la aplicación entre varios servidores, las conexiones de SignalR suelen requerir afinidad de sesión, salvo que usemos un servicio que gestione ese escalado, como Azure SignalR Service.
Si es una aplicación WebAssembly standalone, la salida final son archivos estáticos.
Eso significa que podemos servirla desde:
- Azure Static Web Apps.
- GitHub Pages.
- Netlify, Vercel o cualquier hosting estático.
- Un servidor Nginx o Apache.
La carpeta que debemos desplegar es wwwroot dentro del resultado publicado. Ahí están los .wasm, .dll, .js, .css y demás archivos que descargará el navegador.
En proyectos con InteractiveWebAssembly o InteractiveAuto, normalmente tenemos una solución con parte servidor y parte cliente.
El proyecto servidor publica la aplicación e incluye los recursos estáticos del cliente. Por tanto, debemos ejecutar dotnet publish sobre el proyecto servidor, no copiar manualmente la salida del proyecto cliente.
Aunque InteractiveAuto puede ejecutar componentes posteriores en WebAssembly, la aplicación sigue necesitando su servidor ASP.NET Core.
Variables y configuración
No publiques secretos dentro del código, en un appsettings.json comprometido en Git ni en archivos servidos al navegador.
En producción, la configuración debe venir de:
- Variables de entorno.
- Secretos del proveedor cloud.
- Azure App Configuration.
- Key Vault u otro almacén de secretos.
- Archivos de configuración no versionados, si el entorno lo permite.
El código debería leer configuración con IConfiguration o con opciones tipadas.
builder.Services.Configure<ApiOptions>(
builder.Configuration.GetSection("Api"));Y luego usar esas opciones desde los servicios.
public class ProductoService
{
private readonly ApiOptions _options;
public ProductoService(IOptions<ApiOptions> options)
{
_options = options.Value;
}
}Cuidado con la base path
Si desplegamos la aplicación en la raíz del dominio, por ejemplo:
https://midominio.com/normalmente no hay mayor misterio.
Si la desplegamos en una subruta:
https://midominio.com/app/debemos configurar correctamente la ruta base. Si no lo hacemos, aparecerán errores típicos: CSS que no carga, rutas rotas y scripts que devuelven un 404.
En Blazor WebAssembly standalone, ajustamos el <base href="/"> de wwwroot/index.html, incluida la barra final. En Blazor Web App, configuramos la ruta base en ASP.NET Core y en el proxy inverso. Además, comprobamos que los recursos estáticos y las rutas usen esa misma base.
Publicar en IIS
IIS sigue siendo una opción habitual en entornos Windows.
Para desplegar una Blazor Web App o una aplicación server-side necesitamos:
- Instalar el ASP.NET Core Hosting Bundle en el servidor.
- Crear un sitio o aplicación en IIS.
- Copiar el contenido de la carpeta
publish. - Configurar el App Pool sin .NET CLR clásico.
- Revisar permisos, HTTPS y variables de entorno.
IIS actúa como proxy inverso y la aplicación se ejecuta con Kestrel por debajo. Es el mismo modelo que cualquier ASP.NET Core moderno.
No compartas el mismo App Pool entre varias aplicaciones ASP.NET Core si necesitas aislarlas. Asigna a cada aplicación su App Pool y sus permisos.
Publicar como estático
Para Blazor WebAssembly standalone, el despliegue puede ser tan simple como subir archivos estáticos.
El punto importante es que el servidor debe devolver index.html para las rutas internas de la SPA. Por ejemplo, si el usuario entra directamente en:
/productos/42el servidor no debe buscar un archivo físico llamado productos/42. Debe devolver index.html y dejar que Blazor resuelva la ruta.
Esto se configura con una regla de fallback o reescritura. El proveedor también debe servir los tipos MIME de los archivos WebAssembly y, cuando corresponda, las versiones comprimidas que genera la publicación.