NEW live workshops: Leaving TIBCO or Solace for NATS Adding an enterprise backbone above MQTT Active/active multi-cloud architectures
All posts

A community member asked how to carry an HTTP-style permission level, such as PermissionLevel: 10, on individual NATS messages and have NATS filter or authorize messages based on that value.

The short answer: NATS supports custom message headers, but NATS permissions are subject-based, not header-based. If you attach authorization metadata to a message header, your application code is responsible for reading that header and deciding what to do.

Can NATS messages have custom headers?

Yes. NATS supports custom message headers. They are conceptually similar to HTTP/MIME-style headers: key-value metadata carried with a message.

For example, using the NATS CLI you can publish a message with a custom header using -H:

Terminal window
nats pub foo.bar hello -H "PermissionLevel:10"

Client libraries generally expose headers as part of the message or publish API. The exact API shape varies by language and client, so check the documentation for the client you are using.

For example, in Go you may encounter APIs that publish a message object containing a Header field. Other clients expose similar concepts using their own language conventions.

One caveat: NATS and JetStream use a set of reserved headers internally, conventionally prefixed with Nats- (for example, Nats-Msg-Id). When defining your own metadata, give custom headers distinct names and avoid that prefix so they do not collide with server- or JetStream-interpreted headers. A name like PermissionLevel is fine.

Can NATS permissions check custom header values?

No. NATS server permissions apply at the subject level.

Subject permissions are configured per user: you can allow or deny a given user — and therefore any connection authenticated as that user — from publishing or subscribing to specific subjects, such as:

1
users.get
2
users.create
3
orders.*
4
admin.>

But NATS does not evaluate arbitrary message headers such as:

1
PermissionLevel: 10

as part of its built-in permission checks.

So a policy like the following is not something the NATS server enforces directly:

Allow this subscriber to receive messages only when PermissionLevel >= 10.

If that metadata is in a message header, enforcement must happen outside the NATS server permission system.

Can NATS filter subscriptions or JetStream consumers by header?

NATS subscription routing is subject-based. JetStream consumer filtering is also based on stream and subject configuration, not arbitrary custom header comparisons.

So while a message can carry headers, those headers are not used by NATS as a general-purpose server-side filtering language.

If you need consumers to receive only certain categories of messages, subjects are usually the first design tool to consider.

Option 1: Keep permission metadata in headers and enforce in application code

If your existing system already has message-level metadata such as an integer permission level, you can carry that value as a NATS header:

1
PermissionLevel: 10

Then subscribers parse the header and decide whether to process, ignore, reject, or forward the message.

A simplified flow might look like this:

  1. Publisher sends a message on users.get with PermissionLevel: 10.
  2. Subscriber receives the message.
  3. Subscriber reads the PermissionLevel header.
  4. Subscriber compares it to its local policy.
  5. Subscriber processes or discards the message.

This approach can be useful when permission metadata is part of the business protocol and must travel with the message.

However, it has an important security tradeoff: the server is not enforcing that header policy for you. Any enforcement must be correct and consistent in every application that consumes the message. If publishers are not fully trusted, you also need to think carefully about who is allowed to set that header and whether the header value can be spoofed.

Option 2: Model authorization boundaries in the subject hierarchy

Because NATS permissions are subject-based, another approach is to encode meaningful access boundaries into the subject design.

For example, instead of a single subject with a header:

1
users.get
2
PermissionLevel: 10

You might consider subjects that reflect the authorization or routing boundary:

1
perm.10.users.get
2
perm.20.users.get

or perhaps a domain-oriented design:

1
public.users.get
2
internal.users.get
3
admin.users.get

Then NATS permissions can allow or deny publish and subscribe access to those subject patterns.

This has a clear benefit: the NATS server can enforce access to subjects. But it also has tradeoffs:

  • Subject design becomes part of your authorization model.
  • You need to avoid creating a subject hierarchy that is hard to maintain.
  • Numeric threshold semantics, such as “10 or higher,” may not map cleanly to subject wildcards.
  • Changing authorization categories may require changes to publishers, subscribers, or permission configuration.

For many systems, coarse-grained authorization belongs naturally in subjects, while fine-grained business rules remain in application code.

Option 3: Use a gateway or service layer to enforce policy

If you are migrating from an HTTP gateway model, another option is to keep enforcement in a gateway or service boundary.

For example:

  1. External or less-trusted clients call a gateway.
  2. The gateway authenticates and authorizes the request.
  3. The gateway publishes to NATS using subjects the backend is allowed to consume.
  4. Backend services trust the gateway’s routing and may still inspect headers for business context.

This keeps the server-side NATS permission model focused on which components can publish or subscribe to which subjects, while application-level policy remains in code designed for that purpose.

This pattern can be especially useful when you need to preserve existing authorization rules during a migration from HTTP request/response to NATS messaging.

Choosing between headers and subjects

A practical rule of thumb:

  • Use subjects for routing and access boundaries that NATS should enforce.
  • Use headers for metadata that applications need to interpret.
  • Use application code or a gateway for policies that require custom comparisons, thresholds, or business logic.

For an integer permission level, the right design depends on what that number means.

If it is mainly metadata that consumers use to decide how to process a message, a header may be reasonable.

If it represents a hard security boundary, relying only on consumers to discard unauthorized messages may not be sufficient. In that case, consider whether the boundary should be represented in the subject hierarchy, enforced before publish, or handled by a trusted service layer.

Example: custom permission header with CLI publish

You can test custom headers with the NATS CLI like this:

Terminal window
nats sub foo.bar

In another terminal:

Terminal window
nats pub foo.bar hello -H "PermissionLevel:10"

The exact display and header access behavior depends on the tool or client you use to receive the message, but the published NATS message can carry that custom header.

In an application, the subscriber would need to inspect the message headers and apply its own rule, such as:

1
if PermissionLevel >= requiredLevel:
2
process message
3
else:
4
ignore or reject message

That comparison is application logic, not a NATS server permission check.

Summary

NATS custom headers are useful for carrying per-message metadata, including values similar to HTTP headers. But NATS does not provide built-in permissioning or filtering based on arbitrary header values.

If you need header-based authorization, implement it in your application or gateway. If you need the NATS server to enforce access, design your subject hierarchy and account/user permissions so that the authorization boundary is represented by subjects.


Want help from the NATS experts? Meet with our architects to get help tailored to your use case and environment.

Get the NATS Newsletter

News and content from across the community


Cancel