Headers vs Body?

Recently at work, while designing an API, we faced the question “should we put the user preferred language in the header or body“?

One of the comments was “If this piece of data will be used in multiple endpoints, then maybe we should put into the header.” That sounded at first a compelling argument, but since this was not the first time such discussion came up, I thought it was worth investigating further and writing about it.

HTTP Specification

First of all, let’s check out what the HTTP protocol says about headers and body.

Headers

After reading through the header’s section, I found the following two parts particularly interesting:

Entity-header fields define metainformation about the entity-body

The extension-header mechanism allows additional entity-header fields to be defined without changing the protocol, but these fields cannot be assumed to be recognizable by the recipient. Unrecognized header fields SHOULD be ignored by the recipient and MUST be forwarded by transparent proxies.

This tells us a couple of things:

Body

The body section of the HTTP specification does not specify what should and should not be inside a request body.

To sum up: header is about metadata, custom headers are allowed and body can be anything.

So what?

How does that help us answering the initial question? In theory, it should be easy: is the information metadata? If yes, then set it as a header. If not, set in the body.

Often the answer is indeed quite obvious. For instance, a correlation id meant for tracing is clearly metadata. That is not always the case though: there are cases when it’s not clear whether a piece of data can be considered metadata or not.

For those cases, I found the following three questions to be helpful when defining whether some given data should be in the header or in the body.

  1. Does the data fits a header already specified in the protocol?

HTTP defines a set of reserved headers (e.g. ETag, Authorization). If the data in question fits one of them, then you should use it for the sake of interoperability. Any device that implements the HTTP protocol will understand it out of the box. The most common case is sending a token (e.g. Bearer, JWT) in the Authorization header.

2. Can the data be used for caching, auth, rate limiting, routing by proxies?

Then the header is a good place, since proxies don’t usually inspect the body. Imagine you need to process user data in a specific geographical location due to data privacy regulations. In those cases, a possible solution is to route the traffic to a specific data center according to the user’s legal entity. That can be done in proxy or gateway using a custom header.

3. Is the data used in most endpoints of your API?

In that case, passing it in the header allows you to write a request interceptor to extract the header in a consistent way for all endpoints. Most web frameworks provide that as a built-in feature. An example for that would be the user ID.

In case the answer for the 3 questions is no, then very likely the data in question is not metadata and should be passed in the body.

What about the question in the beginning of this article?

In our case, the user’s preferred language:

The Accept-Language request-header field is similar to Accept, but restricts the set of natural languages that are preferred as a response to the request

Since all three questions were answered with no, we set it in the body.

Conclusion

According to the HTTP protocol specification, the header is about metadata and the body can be anything. The definition of metadata leaves room for discussion. When you are not sure whether something is metadata, keep in mind the following points:

What about you?

How do you approach this question at your job? Did I miss or misinterpret something in specification?

Please drop a comment below, I would love to see how this could be approached differently.

Feedback to the text itself, any typos? Please don’t hesitate to point out.

All about backend development. 🇧🇷 Versão brasileira https://codealbr.medium.com/