This post shows you how to set up CORS in Umbraco

Why might you need it?

If you are going to be using the headless APIs in Umbraco 12 then you might find that the API calls are failing, like I did.

To fix that you might need to add a CORS policy.

After doing some digging and asking some friends in the community, I have found this solution, you do it all in the Startup.cs file:

1. Add a private variable to contain the name of the CORS policy.

namespace MyProject
{
    public class Startup
    {
        private readonly IWebHostEnvironment _env;
        private readonly IConfiguration _config;
        private readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

2. In the ConfigureServices method, add the Cors service:

public void ConfigureServices(IServiceCollection services)
{
    services.AddCors(options =>
    {
        options.AddPolicy(name: MyAllowSpecificOrigins,
            policy  =>
            {
                policy.WithOrigins("https://headlesstest.localtest.me");
            });
    });

    services.AddUmbraco(_env, _config)
        .AddBackOffice()
        .AddWebsite()
        .AddDeliveryApi()
        .AddComposers()
        .Build();
}

3. Now we need to tell it to use Cors and this is the most important part

The order of where we call this really matters. It has to be called directly between .UseRouting(); and .UseAuthentication();

So to achieve this we modify the Configure method, to give us more control.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseUmbraco()
        .WithCustomMiddleware(u =>
        {
            u.RunPrePipeline();

            u.UseUmbracoCoreMiddleware();
            u.AppBuilder.UseUmbracoMediaFileProvider();
            u.AppBuilder.UseStaticFiles();
            u.AppBuilder.UseUmbracoPluginsStaticFiles();
            u.AppBuilder.UseRouting();
            u.AppBuilder.UseCors(MyAllowSpecificOrigins);
            u.AppBuilder.UseAuthentication();
            u.AppBuilder.UseAuthorization();
            u.AppBuilder.UseRequestLocalization();
            u.AppBuilder.UseSession();

            u.RunPostPipeline();
            u.UseBackOffice();
            u.UseWebsite();
        })
        .WithEndpoints(u =>
        {
            u.UseInstallerEndpoints();
            u.UseBackOfficeEndpoints();
            u.UseWebsiteEndpoints();
        });
}

Here is the complete file:

namespace MyProject
{
    public class Startup
    {
        private readonly IWebHostEnvironment _env;
        private readonly IConfiguration _config;
        private readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

        /// <summary>
        /// Initializes a new instance of the <see cref="Startup" /> class.
        /// </summary>
        /// <param name="webHostEnvironment">The web hosting environment.</param>
        /// <param name="config">The configuration.</param>
        /// <remarks>
        /// Only a few services are possible to be injected here https://github.com/dotnet/aspnetcore/issues/9337.
        /// </remarks>
        public Startup(IWebHostEnvironment webHostEnvironment, IConfiguration config)
        {
            _env = webHostEnvironment ?? throw new ArgumentNullException(nameof(webHostEnvironment));
            _config = config ?? throw new ArgumentNullException(nameof(config));
        }

        /// <summary>
        /// Configures the services.
        /// </summary>
        /// <param name="services">The services.</param>
        /// <remarks>
        /// This method gets called by the runtime. Use this method to add services to the container.
        /// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940.
        /// </remarks>
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddCors(options =>
            {
                options.AddPolicy(name: MyAllowSpecificOrigins,
                    policy  =>
                    {
                        policy.WithOrigins("https://headlesstest.localtest.me");
                    });
            });

            services.AddUmbraco(_env, _config)
                .AddBackOffice()
                .AddWebsite()
                .AddDeliveryApi()
                .AddComposers()
                .Build();
        }

        /// <summary>
        /// Configures the application.
        /// </summary>
        /// <param name="app">The application builder.</param>
        /// <param name="env">The web hosting environment.</param>
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseUmbraco()
                .WithCustomMiddleware(u =>
                {
                    u.RunPrePipeline();

                    u.UseUmbracoCoreMiddleware();
                    u.AppBuilder.UseUmbracoMediaFileProvider();
                    u.AppBuilder.UseStaticFiles();
                    u.AppBuilder.UseUmbracoPluginsStaticFiles();
                    u.AppBuilder.UseRouting();
                    u.AppBuilder.UseCors(MyAllowSpecificOrigins);
                    u.AppBuilder.UseAuthentication();
                    u.AppBuilder.UseAuthorization();
                    u.AppBuilder.UseRequestLocalization();
                    u.AppBuilder.UseSession();

                    u.RunPostPipeline();
                    u.UseBackOffice();
                    u.UseWebsite();
                })
                .WithEndpoints(u =>
                {
                    u.UseInstallerEndpoints();
                    u.UseBackOfficeEndpoints();
                    u.UseWebsiteEndpoints();
                });
        }
    }
}

Thanks to Mike Chambers and Poornima Nayar for helping me with this.

Paul Seal

Umbraco MVP and .NET Web Developer from Derby (UK) who specialises in building Content Management System (CMS) websites using MVC with Umbraco as a framework. Paul is passionate about web development and programming as a whole. Apart from when he's with his wife and son, if he's not writing code, he's thinking about it or listening to a podcast about it.

Proudly sponsored by

Moriyama

  • Moriyama build, support and deploy Umbraco, Azure and ASP.NET websites and applications.
AppVeyor

  • CI/CD service for Windows, Linux and macOS
  • Build, test, deploy your apps faster, on any platform.
elmah.io

  • elmah.io is the easy error logging and uptime monitoring service for .NET.
  • Take back control of your errors with support for all .NET web and logging frameworks.
uSync Complete

  • uSync.Complete gives you all the uSync packages, allowing you to completely control how your Umbraco settings, content and media is stored, transferred and managed across all your Umbraco Installations.
uSkinned

  • More than a theme for Umbraco CMS, take full control of your content and design with a feature-rich, award-nominated & content editor focused website platform.
UmbHost

  • Affordable, Geo-Redundant, Umbraco hosting which gives back to the community by sponsoring an Umbraco Open Source Developer with each hosting package sold.