Open Closed

Running Angular/C# on Azure Service App as Spa app #10054


User avatar
0
FarReach.Ezra created

Hi, I am trying to run the latest version of abp.io as a spa on an azure app service. We have done this with older versions successfully (Angular/Auth/Api on same instance) and are assuming it could also be done with the latest but are having much trouble getting it to work and hope you can point us in the right direction.

Things we have done:

  1. Copied published angular code within wwwroot of Host project.
  2. Updated all urls in appsettings.json to point to same azure app service url.
  3. Tried various code snippets for modifying OnApplicationInitialization to use app.UseStaticFiles() and MapFallbackToFile("index.html").
  4. Modified environment.ts urls to point to azure app service.

Seems no matter what we tried we always get the api login page instead of the angular app. Are there any detailed instructions to correctly set this up?

Thanks, Ezra


2 Answer(s)
  • User Avatar
    0
    AI-Bot created
    Support Team ABP Agent
    1. Solution Here’s a verified way to run the ABP Angular SPA and the ASP.NET Core HttpApi.Host on the same Azure App Service (single site, SPA served by the Host). The key points are: publish Angular into the Host’s wwwroot, serve static files, and make SPA fallback return index.html so you don’t see the API login page.

    A) Build and place Angular into Host/wwwroot

    • In the angular project:
      • yarn
      • ng build --configuration production
    • Copy the contents of angular/dist/<your-app> into aspnet-core/src/YourProject.HttpApi.Host/wwwroot
      • Ensure index.html and all assets are directly under wwwroot (not in a nested wwwroot/wwwroot).

    B) Ensure Host serves static files and SPA fallback In YourProjectHttpApiHostModule.cs (or Program.cs if using minimal hosting), inside OnApplicationInitialization (order matters):

    public override void OnApplicationInitialization(ApplicationInitializationContext context)
    {
        var app = context.GetApplicationBuilder();
        var env = context.GetEnvironment();
    
        app.UseAbpRequestLocalization();
    
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
    
        app.UseAbpSecurityHeaders();
        app.UseHttpsRedirection();
        app.UseCorrelationId();
    
        // Serve SPA static files (Angular build in wwwroot)
        app.UseStaticFiles();
    
        app.UseRouting();
    
        app.UseAuthentication();
        app.UseAbpOpenIddictValidation();
        app.UseAuthorization();
    
        app.UseSwagger();
        app.UseAbpSwaggerUI(options =>
        {
            options.SwaggerEndpoint("/swagger/v1/swagger.json", "API");
        });
    
        app.UseAuditing();
        app.UseAbpSerilogEnrichers();
    
        app.UseConfiguredEndpoints();
    
        // SPA fallback: return index.html for non-file, non-API requests
        app.Use(async (ctx, next) =>
        {
            await next();
    
            if (ctx.Response.StatusCode == 404 &&
                !System.IO.Path.HasExtension(ctx.Request.Path.Value) &&
                !ctx.Request.Path.Value.StartsWith("/api", StringComparison.OrdinalIgnoreCase))
            {
                ctx.Request.Path = "/index.html";
                ctx.Response.StatusCode = 200;
                await next();
            }
        });
    }
    

    Alternative controller-based redirect (older practice that also works on Azure App Service):

    public class HomeController : AbpController
    {
        public ActionResult Index()
        {
            return Redirect("~/index.html" + HttpContext.Request.QueryString);
        }
    }
    

    C) Azure App Service specifics

    • Do NOT put Angular under wwwroot/wwwroot. It must be under the Host’s physical wwwroot (the app’s content root path). If you see 302 -> /?httpStatusCode=404 loops, your index.html is likely not being found at the correct path.
    • You typically don’t need IIS URL Rewrite when you handle SPA fallback in middleware as above.
    • Make sure the site runs in HTTPS publicly. If you are behind Azure’s reverse proxy, all OpenID Connect endpoints must be https in the discovery document. If needed, force https scheme before the pipeline:
    app.Use(async (ctx, next) =>
    {
        ctx.Request.Scheme = "https";
        await next();
    });
    

    D) Unified domain configuration Set host appsettings.json consistently for single-domain SPA+API:

    • App
      • SelfUrl: https://your-app.azurewebsites.net
      • AngularUrl: https://your-app.azurewebsites.net
      • CorsOrigins: https://your-app.azurewebsites.net
      • RedirectAllowedUrls: https://your-app.azurewebsites.net
    • AuthServer
      • Authority: https://your-app.azurewebsites.net
      • RequireHttpsMetadata: true (recommended for production)

    E) Angular environment configuration environment.prod.ts:

    • baseUrl: 'https://your-app.azurewebsites.net'
    • oAuthConfig:
      • issuer: 'https://your-app.azurewebsites.net'
      • redirectUri: 'https://your-app.azurewebsites.net'
      • clientId: '<YourApp_App>'
      • responseType: 'code'
      • scope: 'offline_access openid profile email role phone <YourApp>'
    • apis.default.url: '' (same-origin)

    F) IdentityServer client records In DbMigrator appsettings (or wherever you seed IdentityServer clients), ensure the SPA client:

    • RootUrl / RedirectUris / PostLogoutRedirectUris include https://your-app.azurewebsites.net
    • AllowedCorsOrigins include https://your-app.azurewebsites.net

    G) Why you were seeing the API login page If index.html isn’t found or served, requests fall through to API endpoints (e.g., /account/login via MVC/Identity) and you see the API login instead of the Angular SPA. The SPA fallback middleware and correct placement of index.html fix this.

    H) Multitenancy with subdomains (optional) If you run tenants on subdomains on the same app service and still serve SPA from the host:

    • Add domain tenant resolver:
    Configure<AbpTenantResolveOptions>(options =>
    {
        options.AddDomainTenantResolver("{0}.your-app.azurewebsites.net");
    });
    
    • If you want “My Linked Users” and tenant switching to work across subdomains, enable multi-domain support (ABP 5.0+):
    Configure<AbpAccountOptions>(options =>
    {
        options.IsTenantMultiDomain = true;
        options.GetTenantDomain = (httpContext, info) =>
            Task.FromResult(string.IsNullOrWhiteSpace(info.Name)
                ? "https://your-app.azurewebsites.net"
                : $"https://{info.Name}.your-app.azurewebsites.net");
    });
    Configure<AbpAccountIdentityServerOptions>(options =>
    {
        options.IsTenantMultiDomain = true;
        options.GetTenantDomain = (httpContext, ctx, info) =>
            Task.FromResult(string.IsNullOrWhiteSpace(info.Name)
                ? "https://your-app.azurewebsites.net"
                : $"https://{info.Name}.your-app.azurewebsites.net");
    });
    
    • When using wildcard subdomains with IdentityServer validators and CORS, use ABP helpers:
      • AddAbpStrictRedirectUriValidator()
      • AddAbpClientConfigurationValidator()
      • AddAbpWildcardSubdomainCorsPolicyService()
    1. Sources
    • Single host serving Angular SPA with fallback example (ABP Support): https://abp.io/support/questions/670
    • Domain tenant resolver and multi-domain login/linked users (ABP Support): https://abp.io/support/questions/1473
    • Handling HTTPS scheme behind reverse proxy for discovery doc (ABP Support): https://abp.io/support/questions/10011

    This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Can you share the Debug logs of your api website? https://abp.io/support/questions/8622/How-to-enable-Debug-logs-for-troubleshoot-problems

    liming.ma@volosoft.com

    Thanks

Boost Your Development
ABP Live Training
Packages
See Trainings
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v10.1.0-preview. Updated on October 30, 2025, 06:33