One small error between frontend, backend and Azure that breaks production

Sometimes production is broken by a misread host or scheme rather than a major architectural mistake. Frontend, backend and Azure then operate with different versions of the same reality.

The full article is available below. Enable JavaScript to use quizzes and simulations.

One small error between frontend, backend and Azure that breaks production

In modern technology projects, it is easy to believe the greatest risk lies in major architectural decisions. Teams discuss patterns, cloud, security, scalability, responsibility boundaries and service contracts. All matter. Yet production often fails because of something much smaller. Small errors that initially look trivial can break production.

Even experienced developers and architects fall into the same trap: assuming a locally working frontend, a responsive backend and a deployment without red errors mean the system is ready. It is not. There is a substantial difference between “works” and “works well in production”.

One particularly deceptive case occurs when frontend, backend and Azure infrastructure operate with different versions of the same reality. Each layer sees the request slightly differently and interprets its host, scheme, origin, cookies and route in its own way. The system technically runs yet feels unstable, inconsistent and unsafe to maintain.

Why this problem is dangerous

Such failures rarely produce one clear message. Instead, scattered symptoms appear:

  • login works only sometimes,
  • refreshing unexpectedly ends the session,
  • the frontend receives 401 despite a successful login,
  • authentication redirects to the wrong address,
  • cookies are visible but absent from subsequent requests,
  • CORS works in one flow but fails in another,
  • everything looks correct locally, while Azure failures recur unpredictably.

This kind of problem destroys trust in the system. Users do not see an incorrectly forwarded host or a backend misreading the scheme behind a reverse proxy. They see one thing: the system behaves inconsistently.

What it looks like in a real project

Consider a typical setup:

  • Angular for the administration frontend,
  • ASP.NET Core for the backend API,
  • Azure App Service for application hosting,
  • Azure Front Door or Application Gateway as the entry layer,
  • a custom domain,
  • authentication using cookies, sessions or an authorization callback.

The diagram looks mature. Local development works. Staging seems fine too. This is where the trap begins: the team assumes the architecture is sound because basic tests passed.

Production quickly shows that this is not enough.

One small error can be sufficient:

  • the backend misreads X-Forwarded-Proto,
  • the application interprets a request as httpalthough the user connected through https,
  • public and internal hosts are treated as identical,
  • the frontend builds API addresses from the wrong source,
  • cookie policies do not match the actual flow,
  • callbacks are generated from the wrong domain.

Each may look harmless alone. Together, they create a system that appears to work while its architecture is already fractured.

The biggest team mistake: treating symptoms instead of causes

The response is familiar: a frontend interceptor gains another exception. The backend gets another if-a. DevOps adds an infrastructure rule. Someone manually sets a URL. Someone else temporarily relaxes security to unblock the release.

This is a dangerous moment.

The team feels something has been fixed, but only symptoms have changed. The root cause remains. The system still lacks a shared understanding of requests. The next deployment, new flow or domain change brings the problem back in another form.

The architecture quickly becomes a structure of workarounds. It seems to function, but nobody wants to touch it without anxiety.

Real tasks reveal where the system breaks

The problem is clearest in actual business and technical tasks, not diagrams.

1. Logging into the administration panel

This should be simple: credentials, authorization and entry to the panel. Instead, it often reveals that the system does not understand its public domain or whether the original traffic used HTTPS.

Symptoms:

  • a cookie is saved but does not return with the next request,
  • the frontend thinks the user is logged in while the backend returns 401,
  • refreshing the panel loses the session,
  • login redirects to the wrong address or host.

This directly harms product trust; it is not a minor inconvenience.

2. Publishing content from the admin panel

Editorial panels often use a separate API path, additional authorization policies and different requests from the public website. This exposes inconsistencies in cookies, hosts and CORS.

Symptoms:

  • saving a draft works locally but fails in production,
  • publishing returns an error despite valid data,
  • the frontend sends a correct request but the backend sees no user in its session context,
  • some admin endpoints work while others seem randomly unstable.

This is a classic sign that the problem lies in how layers interpret requests and authorization rather than in the form itself.

3. Callbacks from external providers

OAuth, payments and external integrations expose the issue even more clearly. If the backend generates a callback from the wrong domain or scheme, the integration begins to fail.

Symptoms:

  • the external service returns to the wrong address,
  • the request returns over HTTP instead of HTTPS,
  • the provider rejects the callback,
  • the flow works in one environment but not another.

There is no room for “perhaps it will work”. Integrations need one consistent understanding of traffic.

4. Deep links and refreshed views

In SPAs, entering a page directly or refreshing a view often exposes the mismatch. Routing, authorization and backend disagree about context. The frontend tries to restore state; the backend sees no session or an invalid origin. The result looks chaotic to the user. This is especially dangerous because simple manual click-through tests often pass while ordinary use fails.

An example of a problem we recently resolved

One concrete problem in this setup occurs when the frontend builds API URLs from the current origin, while the backend configures handling too late for ForwardedHeaders. It may work locally, but behind a proxy and a production domain it creates confusion.

The incorrect frontend pattern
const apiUrl = `${window.location.origin}/api`;

It looks convenient locally. Problems begin when:

  • the panel uses one domain,
  • the API uses another,
  • traffic passes through a proxy,
  • the public host differs from the internal host.
A better, more stable version
export interface AppEnvironment {
  production: boolean;
  apiBaseUrl: string;
}

export const environment: AppEnvironment = {
  production: true,
  apiBaseUrl: 'https://api.twojadomena.pl'
};

@Injectable({ providedIn: 'root' })
export class ApiConfigService {
  readonly baseUrl = environment.apiBaseUrl.replace(/\/+$/, '');
}
An interceptor that normalizes the address instead of guessing
import { HttpInterceptorFn } from '@angular/common/http';
import { inject } from '@angular/core';

export const apiBaseUrlInterceptor: HttpInterceptorFn = (req, next) => {
  if (/^https?:\/\//i.test(req.url)) {
    return next(req);
  }

  const config = inject(ApiConfigService);
  const normalizedUrl = `${config.baseUrl}/${req.url.replace(/^\/+/, '')}`;

  return next(
    req.clone({
      url: normalizedUrl,
      withCredentials: true
    })
  );
};

The other half of the problem was in the backend. If handling for UseForwardedHeaders() comes too late, subsequent middleware already operates on an incorrect picture of the request.

The right direction in ASP.NET Core
using Microsoft.AspNetCore.HttpOverrides;

builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
    options.ForwardedHeaders =
        ForwardedHeaders.XForwardedFor |
        ForwardedHeaders.XForwardedProto |
        ForwardedHeaders.XForwardedHost;

    options.KnownNetworks.Clear();
    options.KnownProxies.Clear();
});

var app = builder.Build();

app.UseForwardedHeaders();

app.UseCors("AdminCors");
app.UseAuthentication();
app.UseAuthorization();

This apparently minor issue can break:

  • login,
  • cookies,
  • redirects,
  • callbacks,
  • link generation,
  • consistency between frontend and backend.

What needs to be done correctly

A proper fix goes beyond adding exceptions. It establishes one shared understanding across the entire flow.

  • Frontend must use explicit environment configuration rather than guess where the API lives.
  • Backend must correctly understand the host, scheme and fact that it is behind a proxy.
  • Azure must be treated as a real architectural layer rather than a neutral transport pipe.
  • Cookies must match the actual flow, not a local assumption about it.
  • CORS must precisely target public domains rather than be loosened until something works.
  • Logs must show what the system actually sees, not what the team assumes.

Only then can the system be considered production-ready.

The critical lesson

Architecture often fails through small, misunderstood assumptions rather than spectacular decisions:

  • the wrong host,
  • the wrong scheme,
  • the wrong middleware order,
  • an unsuitable cookie policy,
  • the belief that Azure changes nothing,
  • too much confidence that local success guarantees production success.

That is why this problem is so deceptive. It starts as a small anomaly, not an obvious architectural failure. Later, it becomes clear that it affects login, routing, callbacks, security and the stability of the entire platform.

Summary

Frontend, backend and Azure can each be designed correctly in isolation yet form an unstable whole. Production rewards consistency as well as good code.

Every layer must share the same understanding for the system to behave reliably. The same host. The same scheme. The same trust rules. The same security semantics. The same view of the request.

One small error can determine whether a user sees a professional platform or a system that only looks good until the first difficult deployment.