Course outline

ASP.NET Core / Advanced: Verification and Reliable Operations / Lesson 16

Deployment and Proxy Trust

Record runtime artifacts, configuration, proxy boundaries, key persistence, health, and shutdown as explicit deployment contracts.

About 5 min readContent reviewed
On this page

Lesson goals

  • Distinguish framework-dependent and self-contained servicing.
  • Trust forwarded headers only from the intended path.
  • Separate local middleware checks from actual deployment evidence.

A published artifact has a runtime target and update owner

Record SDK, target framework, language version, build configuration, runtime identifier where required, dependencies, and publish mode. Framework-dependent deployment needs a compatible supported runtime. Self-contained deployment carries runtime components that require rebuilding/releasing to service them. Installing a newer SDK on a developer machine does not automatically patch an already deployed artifact.

The illustrative publish command remains non-runnable. It creates no artifact in this course and deploys nothing. Trimming/native AOT can impose additional reflection/metadata and compatibility requirements; successful ordinary JIT component execution does not validate those modes. Review current support and the actual operating-system/runtime contract.

bash Illustrative framework-dependent publish command
dotnet publish -c Release -f net10.0 --self-contained false

This command is not executed; no application artifact is published or deployed.

Forwarded headers cross a trust boundary

A reverse proxy can terminate TLS and forward requests so the application sees a different transport scheme, host, or client address. These values affect redirects, secure cookies, links, auth callbacks, and logging. Do not trust arbitrary client-supplied forwarded headers. Configure known proxies/networks and forwarding limits according to the actual deployment chain.

Recent supported ASP.NET Core servicing behavior ignores forwarded headers from unknown proxies under the documented contract. The .NET 10 baseline must be tested with its current options rather than advice to clear every trust list. Host filtering and forwarded-host allowances have their own purposes; one setting is not universal proof against spoofing.

Exercise trusted and untrusted synthetic peers

The complete program invokes ForwardedHeadersMiddleware directly for two reserved documentation addresses. The configured trusted peer can supply https; the untrusted peer cannot change the assigned http scheme. This tests the middleware/options combination in memory, not a real proxy, certificate, socket, or public deployment. No traffic is sent to either address.

Record the exact proxy path and test negative spoofing cases in approved integration. Incorrect scheme can cause redirect loops or insecure-cookie behavior, while trusting every source can let callers spoof security-relevant information. Diagnose the missing/wrong boundary rather than disabling protections until the symptom disappears.

csharp Invoke forwarding middleware without a network
using System;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;

var options = new ForwardedHeadersOptions { ForwardedHeaders = ForwardedHeaders.XForwardedProto };
options.KnownProxies.Add(IPAddress.Parse("192.0.2.10"));
var middleware = new ForwardedHeadersMiddleware(_ => Task.CompletedTask, NullLoggerFactory.Instance, Options.Create(options));
string untrusted = await CheckAsync("203.0.113.7");
string trusted = await CheckAsync("192.0.2.10");
Console.WriteLine($"untrusted={untrusted} trusted={trusted}");

async Task<string> CheckAsync(string peer)
{
    var context = new DefaultHttpContext();
    context.Connection.RemoteIpAddress = IPAddress.Parse(peer);
    context.Request.Scheme = "http";
    context.Request.Headers["X-Forwarded-Proto"] = "https";
    await middleware.Invoke(context);
    return context.Request.Scheme;
}
Expected output
untrusted=http trusted=https

Only the configured synthetic proxy changes scheme; the test opens no listener or connection.

Replicas need compatible configuration and key lifetimes

Cookie and other data-protection features can depend on key persistence, application isolation, and access across replicas/restarts. Ephemeral local keys can break continuity; a shared key store with wrong scope can create another trust problem. Choose supported secure storage and permissions deliberately. This course never reads or generates real production secrets or key material.

Validate configuration before accepting requests, preserve environment-specific logging/error behavior, and keep secrets out of images and public diagnostics. A launchSettings development port is not a production binding contract. Verify actual host/proxy/container settings rather than copying a local URL into a release note.

Deployment includes startup, drain, and rollback

Plan readiness, traffic admission, graceful shutdown, background cancellation/join, connection draining, and migration compatibility. A rollback may need more than replacing binaries if schema or external effects changed. Health/probe wiring and signal delivery must be verified in the intended isolated deployment environment, not inferred from a direct component test.

Record which artifact and environment were tested, what dependency/version assumptions remain, and how runtime patches are applied. A successful build is a concrete reviewable artifact, not authorization to deploy. All deployment commands/configuration remain illustrative here, and no configured application/server or infrastructure is changed.

Common errors and fixes

All forwarded headers are trusted to fix a redirect loop.

Why it happens
A symptom is removed by widening a security boundary.
How to fix it
Configure the actual trusted proxy chain and verify scheme/host behavior.
Prevent it next time
Test an untrusted peer explicitly.

A self-contained artifact is assumed patched by a host runtime update.

Why it happens
Bundled runtime servicing was omitted.
How to fix it
Rebuild/release under the approved supported patch process.
Prevent it next time
Record runtime components in artifact metadata.

A health route is mistaken for a tested deployment probe.

Why it happens
Endpoint code and environment wiring were conflated.
How to fix it
Verify actual probe path, auth, timing, and orchestrator semantics.
Prevent it next time
Keep deployment evidence separate from component tests.

Check your understanding

1. Why can a synthetic trusted-proxy test pass while a real deployment still redirects incorrectly?

Show worked solution

The actual peer address/network, number of hops, forwarded header names, scheme/host configuration, TLS termination, and middleware order may differ. The test establishes one explicit in-memory option set only. Validate the deployed chain in an authorized isolated environment and keep spoofing protection. Do not clear trust boundaries merely to match a local happy path.

2. What must be considered when several replicas use cookie authentication across restarts?

Show worked solution

The data-protection key store, application isolation name, permissions, rotation/retention, clock/lifetime policy, and compatible auth configuration must support the intended sharing. A working cookie on one process is not evidence that another replica can validate it safely. Test continuity and isolation with synthetic identities under an approved deployment setup; do not expose real key material in tests or logs.

Key takeaways

  • Artifact servicing and deployment mode require explicit ownership.
  • Forwarded headers are trusted only under a reviewed proxy contract.
  • A direct middleware test is not a deployed TLS/proxy/probe test.

Official references