# Reading and writing message headers NServiceBus provides multiple ways to read and write the headers associated with each message. The same approach is used for [built-in NServiceBus headers](/nservicebus/messaging/headers.md) and for custom headers. Custom headers are useful for storing infrastructure-level information not directly related to the business message. Instead of forcing all message types to inherit from a base class or implement a specific interface to ensure the existence of certain properties, consider moving this information into message headers. Message headers are best manipulated through [pipeline behaviors](/nservicebus/pipeline/manipulate-with-behaviors.md), however they can be accessed and modified from message handlers and saga handlers as well. Depending on the [message transport](/transports/index.md), headers are stored with the message either as native headers (if supported) or via a serialized collection of key/value pairs within the message body itself. This article covers the various ways to manipulate message headers. ## Reading incoming headers Headers can be read for an incoming message. ### From a behavior ```cs public class IncomingBehavior : Behavior { public override Task Invoke(IIncomingPhysicalMessageContext context, Func next) { var headers = context.Message.Headers; var nsbVersion = headers[Headers.NServiceBusVersion]; var customHeader = headers["MyCustomHeader"]; return next(); } } ``` ### From a mutator ```cs public class MutateIncomingTransportMessages : IMutateIncomingTransportMessages { public Task MutateIncoming(MutateIncomingTransportMessageContext context) { var headers = context.Headers; var nsbVersion = headers[Headers.NServiceBusVersion]; var customHeader = headers["MyCustomHeader"]; return Task.CompletedTask; } } ``` ### From a handler ```cs public class ReadHandler : IHandleMessages { public Task Handle(MyMessage message, IMessageHandlerContext context) { var headers = context.MessageHeaders; var nsbVersion = headers[Headers.NServiceBusVersion]; var customHeader = headers["MyCustomHeader"]; return Task.CompletedTask; } } ``` ## Writing outgoing headers Headers can be written for an outgoing message. ### From a behavior ```cs public class OutgoingBehavior : Behavior { public override Task Invoke(IOutgoingPhysicalMessageContext context, Func next) { var headers = context.Headers; headers["MyCustomHeader"] = "My custom value"; return next(); } } ``` ### From a mutator ```cs public class MutateOutgoingTransportMessages : IMutateOutgoingTransportMessages { public Task MutateOutgoing(MutateOutgoingTransportMessageContext context) { context.OutgoingHeaders["MyCustomHeader"] = "My custom value"; return Task.CompletedTask; } } ``` ### From a handler > [!NOTE] > SendOptions is meant for custom headers - changes to NServiceBus headers may be overwritten. ```cs public class WriteHandler : IHandleMessages { public async Task Handle(MyMessage message, IMessageHandlerContext context) { var sendOptions = new SendOptions(); sendOptions.SetHeader("MyCustomHeader", "My custom value"); await context.Send(new SomeOtherMessage(), sendOptions); var replyOptions = new ReplyOptions(); replyOptions.SetHeader("MyCustomHeader", "My custom value"); await context.Reply(new SomeOtherMessage(), replyOptions); var publishOptions = new PublishOptions(); publishOptions.SetHeader("MyCustomHeader", "My custom value"); await context.Publish(new SomeOtherMessage(), publishOptions); } } ``` ### For all outgoing messages NServiceBus supports registering headers at configuration time that are then added to all outgoing messages for the endpoint. ```cs endpointConfiguration.AddHeaderToAllOutgoingMessages("MyGlobalHeader", "some static value"); ```