Production Deployment
This page covers the production-specific concerns you need to address before deploying a BFF host. For middleware pipeline order, see Middleware Pipeline.
Load Balancing and Sticky Sessions
Section titled “Load Balancing and Sticky Sessions”The BFF uses ASP.NET Core’s Data Protection to encrypt and sign session cookies. In a multi-instance (load-balanced) deployment, all instances must share the same Data Protection key ring — otherwise cookies issued by one instance cannot be decrypted by another, causing random logout on failover.
Shared Key Storage
Section titled “Shared Key Storage”Configure Data Protection to store keys in a shared location accessible to all instances:
// Using Azure Blob Storage + Azure Key Vaultbuilder.Services.AddDataProtection() .PersistKeysToAzureBlobStorage(connectionString, "data-protection", "keys.xml") .ProtectKeysWithAzureKeyVault(keyIdentifier, credential);
// Using a network file share or a databasebuilder.Services.AddDataProtection() .PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\dp-keys")) .ProtectKeysWithCertificate(certificate);Server-Side Sessions (Recommended for Multi-Instance)
Section titled “Server-Side Sessions (Recommended for Multi-Instance)”With cookie-only sessions, every instance must share Data Protection keys. With server-side sessions, the cookie only holds an opaque session ID — the session payload is stored in a shared database. This is simpler to operate because:
- Key ring only needs to be consistent (not necessarily shared) — the cookie just holds an ID
- Sessions can be inspected and revoked server-side
- Cookie size is minimized
Sticky Sessions
Section titled “Sticky Sessions”If you cannot use server-side sessions and cannot share a Data Protection key ring, configure your load balancer to route each user consistently to the same instance (“sticky sessions” / session affinity). This is a last resort — prefer shared key storage.
Health Check Endpoints
Section titled “Health Check Endpoints”Expose a health check endpoint so your load balancer and orchestrator (Kubernetes, etc.) can detect unhealthy instances:
builder.Services.AddHealthChecks();
// In your pipeline (after UseRouting):app.MapHealthChecks("/health");For a more complete health check that validates downstream dependencies (database, token endpoint reachability):
builder.Services.AddHealthChecks() .AddDbContextCheck<SessionDbContext>() // EF Core session store .AddUrlGroup(new Uri("https://idp.example.com/.well-known/openid-configuration"), name: "identity-provider");
app.MapHealthChecks("/health/live", new HealthCheckOptions { Predicate = _ => false });app.MapHealthChecks("/health/ready", new HealthCheckOptions());/health/live— liveness: is the process running? (no dependency checks)/health/ready— readiness: are all dependencies reachable? (fail this to take the instance out of rotation)
Cookie Domain Configuration
Section titled “Cookie Domain Configuration”By default, the BFF session cookie is scoped to the exact host. If you need the cookie to work across subdomains (e.g. app.example.com and api.example.com):
builder.Services.AddAuthentication() .AddCookie(options => { options.Cookie.Domain = ".example.com"; // Note leading dot options.Cookie.SameSite = SameSiteMode.Strict; options.Cookie.SecurePolicy = CookieSecurePolicy.Always; });For split-host deployments (frontend on app.example.com, BFF on bff.example.com), you will need to set the cookie domain AND configure CORS. See Separate Host for UI and the SplitHosts sample.
Reverse Proxy Configuration
Section titled “Reverse Proxy Configuration”The BFF is typically deployed behind a reverse proxy (NGINX, Azure Application Gateway, AWS ALB, etc.). Configure ASP.NET Core to trust forwarded headers:
builder.Services.Configure<ForwardedHeadersOptions>(options =>{ options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; // Restrict to your proxy's IP to prevent header spoofing: options.KnownProxies.Add(IPAddress.Parse("10.0.0.100"));});
// Must be the FIRST middleware:app.UseForwardedHeaders();Without this, HttpContext.Request.Scheme will be http even when the client uses HTTPS, which causes:
- The OIDC redirect URI to be
http://...(rejected by the identity provider) - The session cookie’s
Secureflag to have no effect HttpContext.Request.Hostto reflect the internal host, breaking the OIDC redirect
Monitoring and Alerting
Section titled “Monitoring and Alerting”BFF emits OpenTelemetry metrics and traces. See Diagnostics for the full list of metric names and activity sources.
Key Metrics to Alert On
Section titled “Key Metrics to Alert On”| Metric | Alert Condition | Likely Cause |
|---|---|---|
session.started | Sudden drop to 0 | Data Protection key mismatch, pod restart without shared keys |
session.ended | Unexpected spike | Back-channel logout sweep, session store purge, Data Protection key rotation |
session.ended / session.started ratio | Sustained ratio > 1 | Sessions ending faster than starting — investigate IdP or store issues |
HTTP 5xx on /bff/* endpoints | Any sustained spike | BFF host error — check logs |
Recommended Alerts
Section titled “Recommended Alerts”# Prometheus-style alert examples# Metric names are converted from dot notation to underscores by Prometheus.
# No new sessions — potential Data Protection key mismatchalert: BffNoNewSessionsexpr: rate(session_started_total[5m]) == 0for: 5m
# Abnormal session churnalert: BffSessionChurnexpr: rate(session_ended_total[5m]) / rate(session_started_total[5m]) > 2for: 5m