Tombatron
Tombatron

Reputation: 1347

Kestrel throwing "Invalid Host header" error while serving traffic

We have recently migrated an ASP.NET MVC 5 application to ASP.NET Core 2.2.

Everything seems to be working fine, however we are getting the following exception on a fairly regular basis (about three times a second, more on that in a moment):

BadHttpRequestException: Invalid Host header: '~^appname.*$'
  Module "Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException", line 0, col 0, in Throw
    Void Throw(Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.RequestRejectionReason, System.String)
  Module "Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Http1Connection", line 95, col 0, in EnsureHostHeaderExists
    Void EnsureHostHeaderExists()
  Module "Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Http1Connection", line 196, col 0, in TryParseRequest
    Boolean TryParseRequest(System.IO.Pipelines.ReadResult, Boolean ByRef)
  Module "Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol+<ProcessRequests>d__185`1", line 170, col 0, in MoveNext
    Void MoveNext()
  Module "System.Runtime.ExceptionServices.ExceptionDispatchInfo", line 12, col 0, in Throw
    Void Throw()
  Module "System.Runtime.CompilerServices.TaskAwaiter", line 46, col 0, in HandleNonSuccessAndDebuggerNotification
    Void HandleNonSuccessAndDebuggerNotification(System.Threading.Tasks.Task)
  Module "Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol+<ProcessRequestsAsync>d__184`1", line 135, col 0, in MoveNext
    Void MoveNext()

In consulting with a member of our ops team it's clear that we have three instances of HAProxy all checking each node of our application about once a second.

Every request to this application would flow as follows:

HAProxy -> Nginx -> Kestrel/ASP.NET Core application

My question is, how can I determine what is going on here?

Upvotes: 5

Views: 1994

Answers (2)

Tombatron
Tombatron

Reputation: 1347

The current version of Kestrel (we're using ASP.NET Core 2.2) does not support HTTP access logging.

See this issue: Support access logging with Common Log Format/Extended Log Format

In order to track down the issue I had to have our ops team review our nginx logs (nginx is the reverse proxy we're using) to determine where requests with the invalid host header were coming from.

The issue turned out to be a mal-configured status checking application.

Upvotes: 3

Vladmir
Vladmir

Reputation: 1265

I guess you should pass there some valid Url, or asterisk, or forward slash

The code responsible for parsing is here https://github.com/aspnet/KestrelHttpServer/blob/b8a1c04ffbeee6c60a74766142ff3e2e6779d701/src/Kestrel.Core/Internal/Http/Http1Connection.cs

        public void OnStartLine(HttpMethod method, HttpVersion version, Span<byte> target, Span<byte> path, Span<byte> query, Span<byte> customMethod, bool pathEncoded)
        {
            Debug.Assert(target.Length != 0, "Request target must be non-zero length");

            var ch = target[0];
            if (ch == ByteForwardSlash)
            {
                // origin-form.
                // The most common form of request-target.
                // https://tools.ietf.org/html/rfc7230#section-5.3.1
                OnOriginFormTarget(method, version, target, path, query, customMethod, pathEncoded);
            }
            else if (ch == ByteAsterisk && target.Length == 1)
            {
                OnAsteriskFormTarget(method);
            }
            else if (target.GetKnownHttpScheme(out var scheme))
            {
                OnAbsoluteFormTarget(target, query);
            }
            else
            {
                // Assume anything else is considered authority form.
                // FYI: this should be an edge case. This should only happen when
                // a client mistakenly thinks this server is a proxy server.
                OnAuthorityFormTarget(method, target);
            }

            Method = method != HttpMethod.Custom
                ? HttpUtilities.MethodToString(method) ?? string.Empty
                : customMethod.GetAsciiStringNonNullCharacters();
            _httpVersion = version;

            Debug.Assert(RawTarget != null, "RawTarget was not set");
            Debug.Assert(Method != null, "Method was not set");
            Debug.Assert(Path != null, "Path was not set");
            Debug.Assert(QueryString != null, "QueryString was not set");
            Debug.Assert(HttpVersion != null, "HttpVersion was not set");
        }

For example, we send in Host header the host of LoadBalancer like dc-lb.company-dc.lan, in your scenario I guess it should be the hostname of your HAProxy instance

Upvotes: 1

Related Questions