# How to Configure Custom HTTP Headers for LLM Requests in ChocolateLMLite

> Learn to configure custom HTTP headers for LLM requests in ChocolateLMLite by extending OpenRouterHttpHandler. Inject headers easily from global settings for seamless API integration.

- Repository: [Segment (gpsnmeajp)/chocolatelmlite](https://github.com/gpsnmeajp/chocolatelmlite)
- Tags: how-to-guide
- Published: 2026-03-02

---

**Extend the `OpenRouterHttpHandler` class to read a dictionary of custom headers from the global settings and inject them into every outgoing HTTP request to your LLM provider.**

ChocolateLMLite routes all LLM API traffic through a centralized HTTP wrapper called **OpenRouterHttpHandler** (located in [`src/OpenRouterHttpHandler.cs`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/src/OpenRouterHttpHandler.cs)). By tapping into the existing configuration pipeline—specifically the `GeneralSettings` model and `FileManager` accessor—you can attach arbitrary headers such as custom `User-Agent` strings, API version markers, or authentication tokens to every request without touching the core generation logic in `gpsnmeajp/chocolatelmlite`.

## How the OpenRouterHttpHandler Manages LLM Requests

All LLM requests in ChocolateLMLite—including calls from **SummaryLlm**, **SearchLlm**, and **ImageGenerater**—flow through a single chokepoint: the `OpenRouterHttpHandler` class in [`src/OpenRouterHttpHandler.cs`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/src/OpenRouterHttpHandler.cs). 

The handler currently injects two static identification headers into every `HttpRequestMessage`:

```csharp
request.Headers.Add("X-Title", "Chocolate LM Lite");
request.Headers.Add("HTTP-Referer", "https://github.com/gpsnmeajp/chocolatelmlite");

```

Because `LLM.GenerateResponseAsync` instantiates this handler (see [`src/LLM.cs`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/src/LLM.cs), lines 31–38), adding custom header logic here automatically applies to **all** LLM traffic. The following steps extend this handler to read user-defined headers from [`settings.json`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/settings.json).

## Step-by-Step Implementation

### Add Custom Headers to settings.json

Locate [`settings.json`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/settings.json) in your runtime directory (next to the executable). Add a `CustomHeaders` dictionary inside the existing `GeneralSettings` object:

```json
{
  "GeneralSettings": {
    "CustomHeaders": {
      "User-Agent": "ChocolateLMLite/1.0",
      "X-Api-Version": "2024-03",
      "Authorization": "Bearer custom-token"
    }
  }
}

```

Keys must be valid HTTP header names; values are inserted verbatim into the request.

### Update the GeneralSettings Model

Open [`src/GeneralSettings.cs`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/src/GeneralSettings.cs) (or the equivalent settings POCO) and add a property to capture the dictionary:

```csharp
public Dictionary<string, string>? CustomHeaders { get; set; }

```

The existing `JsonSerializer` configuration automatically populates this property when [`settings.json`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/settings.json) is loaded.

### Expose Headers via FileManager

In [`src/FileManager.cs`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/src/FileManager.cs), add a public accessor method to surface the dictionary to the HTTP handler:

```csharp
public IReadOnlyDictionary<string, string>? GetCustomHeaders()
{
    return generalSettings?.CustomHeaders;
}

```

This method leverages the already-deserialized `generalSettings` instance used throughout the project.

### Inject Headers in OpenRouterHttpHandler.cs

Edit [`src/OpenRouterHttpHandler.cs`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/src/OpenRouterHttpHandler.cs) and modify the `SendAsync` method. After the two fixed headers, iterate over the custom dictionary and add each entry:

```csharp
protected override async Task<HttpResponseMessage> SendAsync(
    HttpRequestMessage request, CancellationToken cancellationToken)
{
    // Fixed headers required by the project
    request.Headers.Add("X-Title", "Chocolate LM Lite");
    request.Headers.Add("HTTP-Referer", "https://github.com/gpsnmeajp/chocolatelmlite");

    // Inject user-defined headers from settings.json
    var customHeaders = _fileManager.GetCustomHeaders();
    if (customHeaders != null)
    {
        foreach (var kvp in customHeaders)
        {
            // Prevent duplicate header exceptions by removing existing values
            if (request.Headers.Contains(kvp.Key))
                request.Headers.Remove(kvp.Key);
            
            request.Headers.Add(kvp.Key, kvp.Value);
        }
    }

    // Continue with existing logic (logging, debug files, etc.)
    return await base.SendAsync(request, cancellationToken);
}

```

Ensure the handler has access to the `FileManager` instance (typically passed via constructor injection or a static accessor depending on your fork).

## Testing and Verification

Run the application and capture an outgoing request using a local proxy such as **mitmproxy**, **Fiddler**, or the built-in debug logs. The request headers should now include your custom entries alongside the default identifiers:

```text
X-Title: Chocolate LM Lite
HTTP-Referer: https://github.com/gpsnmeajp/chocolatelmlite
User-Agent: ChocolateLMLite/1.0
X-Api-Version: 2024-03

```

If headers are missing, verify that:
- [`settings.json`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/settings.json) contains valid JSON with no trailing commas
- `FileManager.GetCustomHeaders()` returns a non-null dictionary at runtime
- The modified assembly is compiled and deployed

## Advanced Configuration: Per-LLM-Type Headers

If you require distinct headers for specific LLM types—such as different `Authorization` tokens for summarization versus image generation—you can extend the pattern:

1. Add separate dictionaries to `GeneralSettings` (e.g., `SummaryHeaders`, `SearchHeaders`, `ImageHeaders`)
2. In [`src/SummaryLlm.cs`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/src/SummaryLlm.cs), [`src/SearchLlm.cs`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/src/SearchLlm.cs), or [`src/ImageGenerater.cs`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/src/ImageGenerater.cs), read the appropriate dictionary from `FileManager`
3. Pass the specific headers into a modified `OpenRouterHttpHandler` constructor or use a factory pattern to instantiate handler variants

This approach maintains clean separation while supporting provider-specific requirements for each LLM workflow.

## Summary

- **Centralized Handler**: All LLM requests pass through [`src/OpenRouterHttpHandler.cs`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/src/OpenRouterHttpHandler.cs), making it the ideal injection point for global headers.
- **Configuration Flow**: Add headers to [`settings.json`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/settings.json) → deserialize into `GeneralSettings` → expose via `FileManager.GetCustomHeaders()` → consume in the HTTP handler.
- **Duplicate Safety**: The implementation removes existing headers before adding custom ones to avoid `ArgumentException` collisions.
- **Universal Coverage**: Because `LLM.GenerateResponseAsync` instantiates the handler, custom headers automatically apply to text generation, search, and image endpoints.

## Frequently Asked Questions

### Where is the HTTP handler instantiated in ChocolateLMLite?

The `OpenRouterHttpHandler` is instantiated inside `LLM.GenerateResponseAsync` (see [`src/LLM.cs`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/src/LLM.cs), lines 31–38). This centralized instantiation ensures that every LLM request—whether for summarization, search, or image generation—routes through the same handler pipeline.

### Can I override the default X-Title and HTTP-Referer headers?

Yes. The custom header loop in `OpenRouterHttpHandler.SendAsync` removes existing headers before adding new ones (`request.Headers.Remove(kvp.Key)`). If you include "X-Title" or "HTTP-Referer" in your [`settings.json`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/settings.json) `CustomHeaders` dictionary, your values will replace the default identifiers.

### What happens if I specify an invalid header name in settings.json?

The `HttpHeaders.Add` method in .NET validates header names at runtime. If [`settings.json`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/settings.json) contains an illegal character (such as a space or non-ASCII symbol in the key name), the application will throw a `FormatException` when attempting to send the first LLM request. Always use RFC-compliant header names.

### Do custom headers apply to image generation requests?

Yes. The `ImageGenerater` class (located in [`src/ImageGenerater.cs`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/src/ImageGenerater.cs)) uses the same `LLM` infrastructure to communicate with the provider. Because `OpenRouterHttpHandler` is the underlying transport layer for all LLM-derived operations, custom headers configured in [`settings.json`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/settings.json) automatically propagate to image generation calls.