El CORS es un mecanismo con el que el servidor autoriza determinados accesos entre orígenes en los navegadores.
Tu API funciona perfecta. Haces las peticiones desde Swagger y te devuelve un 200 OK. Haces las peticiones desde Postman y funciona de maravilla.
Te vas a tu proyecto de React/Angular, haces un fetch a la misma URL y… ¡BAM! Consola llena de letras rojas:
Access to fetch at ‘https://mi-api.com’ from origin ‘http://localhost:3000’ has been blocked by CORS policy.
¿Por qué tu API odia a tu Frontend? ¿Por qué Postman sí puede y Chrome no? No es que tu API falle. Es que el navegador te está “protegiendo” (aunque a veces parezca que solo quiere molestarte).
Vamos a entender por qué aparece el bloqueo y cómo configurarlo en ASP.NET Core.
Qué es la política del mismo origen
Para entender CORS, primero hay que entender su opuesto: la Política del Mismo Origen (Same-Origin Policy).
Por seguridad, los navegadores impiden por defecto que una web cargada en el Origen A (ej: localhost:3000) lea datos de un servidor en el Origen B (ej: localhost:5000).
Un “Origen” se define por: Protocolo + Dominio + Puerto.
http://example.comyhttps://example.comson orígenes distintos (protocolo).localhost:3000ylocalhost:5000son orígenes distintos (puerto).
Si no existiera esta regla, una web maliciosa podría intentar hacer peticiones a facebook.com en segundo plano con tus cookies y robarte la sesión.
CORS al rescate
CORS (Cross-Origin Resource Sharing) es el mecanismo estándar para relajar esta seguridad de forma controlada.
Es la forma que tiene el servidor (tu API) de decirle al navegador: “Tranquilo, conozco a los chicos de localhost:3000, déjales pasar”.
Importante: CORS es una política aplicada por los navegadores. Postman o una app móvil no la aplican, por eso no sufren este bloqueo.
CORS no sustituye a la autenticación ni impide que un cliente ajeno al navegador llame a la API.
Configurar CORS en ASP.NET Core
En .NET, configurar esto es un proceso de dos pasos: definir la Política (Servicios) y activar el Middleware.
Definir la política (builder.Services)
En Program.cs, antes de builder.Build(), definimos quién puede entrar.
var builder = WebApplication.CreateBuilder(args);
// Definimos una constante para el nombre, para no equivocarnos luego
var MisOrigenesPermitidos = "_misOrigenesPermitidos";
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MisOrigenesPermitidos,
policy =>
{
policy.WithOrigins("http://localhost:3000",
"https://mi-web-produccion.com")
.AllowAnyHeader()
.AllowAnyMethod();
});
});WithOrigins: La lista blanca de dominios. No pongas/al final.AllowAnyHeader: Importante si usas JWT. Si no lo pones, el navegador bloqueará la cabeceraAuthorizationy el login fallará.AllowAnyMethod: Permite GET, POST, PUT, DELETE, etc.
Activar el middleware (app.UseCors)
En este punto la mayoría falla. El orden es crítico.
CORS debe ejecutarse después de UseRouting y antes de UseAuthorization. Es habitual colocarlo también antes de UseAuthentication, como en este ejemplo.
El navegador puede enviar una petición previa OPTIONS (preflight) para preguntar si el método y las cabeceras están permitidos. El middleware de CORS debe poder resolverla antes de que la autorización proteja el endpoint.
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
// ✅ AQUÍ: Justo antes de la seguridad
app.UseCors(MisOrigenesPermitidos);
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();Desarrollo y producción
Durante el desarrollo, a veces es tedioso estar añadiendo cada puerto nuevo. Podemos ser más permisivos SOLO en desarrollo.
if (app.Environment.IsDevelopment())
{
// "Barra libre" para desarrollo
app.UseCors(x => x
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader());
}
else
{
// Política restrictiva para producción
app.UseCors(MisOrigenesPermitidos);
}Peligro: No uses AllowAnyOrigin() como solución por defecto en producción si tu API maneja datos sensibles. Define orígenes concretos y revisa especialmente los casos con cookies o credenciales.
El caso especial de las credenciales
Si en lugar de JWT (Header) decides usar Cookies de sesión o autenticación de Windows, la configuración cambia. El navegador es más estricto aún.
- El frontend debe enviar
withCredentials: true. - El backend NO puede usar
AllowAnyOrigin(). Debe especificar el origen exacto. - El backend debe añadir
.AllowCredentials().
policy.WithOrigins("http://localhost:3000") // Origen explícito obligatorio
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials(); // 👈 Permite pasar CookiesDiagnosticar problemas de CORS
Si sigues viendo el error rojo:
- Mira la pestaña Network: Busca la petición fallida. ¿Es la petición real o una petición de tipo
OPTIONS? - Revisa el orden: ¿Está
UseCorsantes deUseAuthorization? - Revisa los Headers: ¿Has puesto
.AllowAnyHeader()? El headerAuthorization(donde va el JWT) suele ser bloqueado si no se permite explícitamente. - Limpia la caché: A veces los navegadores guardan las respuestas de CORS. Abre una ventana de incógnito.