DEV Community

Hasan Safwan
Hasan Safwan

Posted on • Originally published at Medium on

Understanding HTTP Request Headers: Classification, Behavior, and How Servers React

Demystifying HTTP for Web Developers — Part 3

HTTP Headers: The Real Drivers of Behavior

In Part 2 of this series, we dissected the structure of an HTTP request — from the request line to headers and message body. We saw how a minimal request could be valid, but real-world HTTP traffic rarely resembles those minimal forms. In practice, requests are filled with headers that instruct servers, inform intermediaries, and enable negotiation.

Request headers are not mere accessories. They are the instruments through which clients express identity, context, preference, and security credentials. They control everything from whether a server compresses a response to whether the request is authorized. Without headers, HTTP cannot fulfill its role as a stateless, flexible, and interoperable protocol.

In this article, we shift our focus from structure to semantics. We’ll analyze headers from three angles:

  • RFC-defined syntax and classification
  • Real client behavior using Postman
  • Real server behavior using ASP.NET Core MVC

The goal is not just to understand what headers exist — but to see how they influence behavior and how a modern web framework processes them in real time.

What Are HTTP Headers?

HTTP headers are textual metadata fields included in request and response messages. Each header is a key-value pair, defined in the format:

field-name: token
Enter fullscreen mode Exit fullscreen mode

This structure is formally defined in RFC 9110 §5.1. Field names are case-insensitive, and whitespace around the colon is ignored. Values can be simple strings or structured lists, depending on the header.

Header field semantics are defined individually across the specification, depending on their purpose. There is no dedicated section in RFC 9110 for general request header semantics; instead, each header is defined in context. For example:

Headers are placed between the request line and the optional body. They are terminated by a blank line (CRLF) that signals the start of the body. In ASP.NET Core, headers are accessible via the Request.Headers dictionary, which automatically handles normalization and case insensitivity.

Here is an example of a request as seen in Postman’s “Console” tab:

GET /api/home HTTP/1.1
User-Agent: PostmanRuntime/7.43.0
Accept: */*
Postman-Token: 865051eb-6af4-4900-b82c-a96172193517
Host: localhost:5000
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Enter fullscreen mode Exit fullscreen mode

ASP.NET Core allows you to inspect them directly:

[HttpGet]
[Route("Inspect")
public IActionResult Inspect()
{
    var userAgent = Request.Headers["User-Agent"].ToString();
    var accept = Request.Headers["Accept"].ToString();
    var correlationId = Request.Headers["X-Correlation-ID"].FirstOrDefault();

    return Ok(new
    {
        UserAgent = userAgent,
        Accept = accept,
        CorrelationId = correlationId
    });
}
Enter fullscreen mode Exit fullscreen mode

Header Classification and Behavior

To reason about headers systematically, it’s useful to classify them based on their function and scope.

1. General Headers

These apply to both requests and responses, and often relate to message handling rather than payload semantics.

  • Cache-Control: controls caching behavior
  • Connection: indicates connection persistence (e.g., keep-alive)

2. Request Headers

Used only in client-to-server communication. These inform the server about the client’s capabilities, preferences, or context.

  • Accept: indicates preferred response formats
  • Accept-Encoding: lists supported compression algorithms
  • User-Agent: identifies client software
  • Authorization: supplies credentials (e.g., Bearer tokens)
  • Referer, Origin: supply context for cross-origin requests

3. Entity Headers

These describe the content of the message body.

  • Content-Type: media type of the payload
  • Content-Length: size in bytes
  • Content-Encoding: compression applied to the body

4. Custom Headers

These are not defined by the core specification but widely used in real-world applications.

  • X-Correlation-ID: used to trace requests across distributed systems
  • X-Requested-With: often used in AJAX requests to differentiate browser types

Observing Header Behavior in ASP.NET Core MVC

Let’s now see how headers affect behavior using Postman and how ASP.NET Core interprets them.

Accept Header and Format Negotiation

Scenario: A client sends Accept: application/json expecting a JSON response.

Postman Request:

GET /api/home HTTP/1.1
Accept: application/json
User-Agent: PostmanRuntime/7.43.0
Postman-Token: 012874d8-ddcd-4f65-9e36-6abd3f97a722
Host: localhost:5000
Enter fullscreen mode Exit fullscreen mode

ASP.NET Core Behavior: By default, ASP.NET Core uses registered output formatters to serialize the response. JSON is the default. If you request text/html, ASP.NET Core may still return JSON unless strict negotiation is enabled.

Without strict negotiation:

With strict negotation:

builder.Services.AddControllers(options =>
{
    options.ReturnHttpNotAcceptable = true;
});
Enter fullscreen mode Exit fullscreen mode

With this option enabled, unsupported formats will result in:

406 Not Acceptable
Enter fullscreen mode Exit fullscreen mode

Content-Type and Model Binding

Scenario: A POST request includes JSON data but does not specify Content-Type.

Postman Body: Raw → JSON

{
    "email": "user@example.com",
    "password": "123"
}
Enter fullscreen mode Exit fullscreen mode

Missing Header: Content-Type: application/json

ASP.NET Core Behavior: Model binding fails. The action may receive a null model or return:

415 Unsupported Media Type
Enter fullscreen mode Exit fullscreen mode

Model binding depends on Content-Type matching a registered input formatter.

Authorization Header

Scenario: A client sends an access token using Authorization: Bearer .

Postman Setup: Use the “Authorization” tab → Bearer Token.

ASP.NET Core Behavior: If JWT authentication is configured, [Authorize] attributes will automatically enforce security. No manual parsing of headers is required.

[Authorize]
[HttpGet("secure")]
public IActionResult SecureEndpoint() => Ok("Access granted.");
Enter fullscreen mode Exit fullscreen mode

Invalid or missing tokens return:

401 Unauthorized
Enter fullscreen mode Exit fullscreen mode

Accept-Encoding and Compression

Scenario: The client requests compressed content.

Header: Accept-Encoding: gzip

ASP.NET Core Setup:

services.AddResponseCompression();
app.UseResponseCompression();
Enter fullscreen mode Exit fullscreen mode

ASP.NET Core automatically compresses responses if the client supports it. You’ll see Content-Encoding: gzip in the response headers and reduced payload size.

X-Correlation-ID and Middleware Handling

Scenario: You want to enforce that a custom header is present on all requests.

Middleware Implementation:

public class CorrelationIdMiddleware
{
    public async Task Invoke(HttpContext context, RequestDelegate next)
    {
        if (!context.Request.Headers.ContainsKey("X-Correlation-ID"))
        {
            context.Response.StatusCode = 400;
            await context.Response.WriteAsync("Missing X-Correlation-ID");
            return;
        }

        await next(context);
    }
}

public class CorrelationIdMiddleware
{
    public async Task Invoke(HttpContext context, RequestDelegate next)
    {
        if (!context.Request.Headers.ContainsKey("X-Correlation-ID"))
        {
            context.Response.StatusCode = 400;
            await context.Response.WriteAsync("Missing X-Correlation-ID");
            return;
        }

        await next(context);
    }
}
Enter fullscreen mode Exit fullscreen mode

This pattern is commonly used for enforcing traceability in distributed environments.

Common Pitfalls and Clarifications

  • Accept is not enforced by default. ASP.NET Core responds with the best available formatter unless explicitly configured.
  • Content-Type must match the request body. Otherwise, model binding fails silently or throws a 415.
  • Custom headers are not validated. You must implement your own enforcement logic.
  • Some headers can be duplicated (e.g., Accept). Others cannot (e.g., Content-Length). Duplicate Content-Length can result in request rejection.
  • Case insensitivity is handled for you. Request.Headers["accept"] and Request.Headers["Accept"] return the same result.

Conclusion

HTTP request headers are not ornamental — they are essential. They determine how content is negotiated, how credentials are passed, how bodies are interpreted, and how APIs are secured. In ASP.NET Core, headers are not only accessible but also actionable through middleware, model binding, and authentication frameworks.

By learning how to inspect and react to headers with precision, you move from merely calling endpoints to truly mastering the protocol beneath your applications.

In Part 4, we’ll shift to the body of the request — exploring payloads, semantics, media types, and how to handle them securely and effectively in modern APIs.

Tiugo image

Modular, Fast, and Built for Developers

CKEditor 5 gives you full control over your editing experience. A modular architecture means you get high performance, fewer re-renders and a setup that scales with your needs.

Start now

Top comments (0)

AWS Security LIVE! Stream

Streaming live from AWS re:Inforce

Tune into Security LIVE! at re:Inforce for expert takes on modern security challenges.

Learn More

👋 Kindness is contagious

Discover this thought-provoking article in the thriving DEV Community. Developers of every background are encouraged to jump in, share expertise, and uplift our collective knowledge.

A simple "thank you" can make someone's day—drop your kudos in the comments!

On DEV, spreading insights lights the path forward and bonds us. If you appreciated this write-up, a brief note of appreciation to the author speaks volumes.

Get Started