Skip to content

Latest commit

 

History

History
364 lines (290 loc) · 16 KB

http-headers.adoc

File metadata and controls

364 lines (290 loc) · 16 KB

REST Basics - HTTP headers

We describe a handful of standard HTTP headers, which we found raising the most questions in our daily usage, or which are useful in particular circumstances but not widely known.

Though we generally discourage usage of proprietary headers, they are useful to pass generic, service independent, overarching information relevant for our specific application architecture. We consistently define these proprietary headers in this section below. Whether services support these concerns or not is optional. Therefore, the OpenAPI API specification is the right place to make this explicitly visible — use the parameter definitions of the resource HTTP methods.

{MAY} use standard headers

Use this list and explicitly mention its support in your OpenAPI definition.

{SHOULD} use kebab-case with uppercase separate words for HTTP headers

This convention is followed by (most of) the standard headers e.g. as defined in {RFC-2616}[RFC 2616] and {RFC-4229}[RFC 4229]. Examples:

If-Modified-Since
Accept-Encoding
Content-ID
Language

Note, HTTP standard defines headers as case-insensitive ({RFC-7230}#page-22[RFC 7230, p.22]). However, for sake of readability and consistency you should follow the convention when using standard or proprietary headers. Exceptions are common abbreviations like ID.

{MUST} use Content-* headers correctly

Content or entity headers are headers with a Content- prefix. They describe the content of the body of the message and they can be used in both, HTTP requests and responses. Commonly used content headers include but are not limited to:

  • {Content-Disposition} can indicate that the representation is supposed to be saved as a file, and the proposed file name.

  • {Content-Encoding} indicates compression or encryption algorithms applied to the content.

  • {Content-Length} indicates the length of the content (in bytes).

  • {Content-Language} indicates that the body is meant for people literate in some human language(s).

  • {Content-Location} indicates where the body can be found otherwise ({MAY} use Content-Location header for more details]).

  • {Content-Range} is used in responses to range requests to indicate which part of the requested resource representation is delivered with the body.

  • {Content-Type} indicates the media type of the body content.

{SHOULD} use Location header instead of Content-Location header

As the correct usage of {Content-Location} response header (see below) with respect to caching and its method specific semantics is difficult, we discourage the use of {Content-Location}. In most cases it is sufficient to inform clients about the resource location in create or re-direct responses by using the {Location} header while avoiding the {Content-Location} specific ambiguities and complexities.

More details in RFC 7231 {RFC-7231}#section-7.1.2[7.1.2 Location], {RFC-7231}#section-3.1.4.2[3.1.4.2 Content-Location]

{MAY} use Content-Location header

{Content-Location} is an optional response header that can be used in successful write operations ({PUT}, {POST}, or {PATCH}) or read operations ({GET}, {HEAD}) to guide caching and signal a receiver the actual location of the resource transmitted in the response body. This allows clients to identify the resource and to update their local copy when receiving a response with this header.

The Content-Location header can be used to support the following use cases:

  • For reading operations {GET} and {HEAD}, a different location than the requested URL can be used to indicate that the returned resource is subject to content negotiations, and that the value provides a more specific identifier of the resource.

  • For writing operations {PUT} and {PATCH}, an identical location to the requested URL can be used to explicitly indicate that the returned resource is the current representation of the newly created or updated resource.

  • For writing operations {POST} and {DELETE}, a content location can be used to indicate that the body contains a status report resource in response to the requested action, which is available at provided location.

Note: When using the {Content-Location} header, the {Content-Type} header has to be set as well. For example:

GET /products/123/images HTTP/1.1

HTTP/1.1 200 OK
Content-Type: image/png
Content-Location: /products/123/images?format=raw

{MAY} consider to support Prefer header to handle processing preferences

The {Prefer} header defined in {RFC-7240}[RFC 7240] allows clients to request processing behaviors from servers. It pre-defines a number of preferences and is extensible, to allow others to be defined. Support for the {Prefer} header is entirely optional and at the discretion of API designers, but as an existing Internet Standard, is recommended over defining proprietary "X-" headers for processing directives.

The {Prefer} header can defined like this in an API definition:

components:
  headers:
  - Prefer:
      description: >
        The RFC7240 Prefer header indicates that a particular server behavior
        is preferred by the client but is not required for successful completion
        of the request (see [RFC 7240](https://tools.ietf.org/html/rfc7240).
        The following behaviors are supported by this API:

        # (indicate the preferences supported by the API or API endpoint)
        * **respond-async** is used to suggest the server to respond as fast as
          possible asynchronously using 202 - accepted - instead of waiting for
          the result.
        * **return=<minimal|representation>** is used to suggest the server to
          return using 204 without resource (minimal) or using 200 or 201 with
          resource (representation) in the response body on success.
        * **wait=<delta-seconds>** is used to suggest a maximum time the server
          has time to process the request synchronously.
        * **handling=<strict|lenient>** is used to suggest the server to be
          strict and report error conditions or lenient, i.e. robust and try to
          continue, if possible.

      type: array
      items:
        type: string
      required: false

Note: Please copy only the behaviors into your {Prefer} header specification that are supported by your API endpoint. If necessary, specify different {Prefer} headers for each supported use case.

Supporting APIs may return the {Preference-Applied} header also defined in {RFC-7240}[RFC 7240] to indicate whether a preference has been applied.

{MAY} consider to support ETag together with If-Match/If-None-Match header

When creating or updating resources it may be necessary to expose conflicts and to prevent the 'lost update' or 'initially created' problem. Following {RFC-7232}[RFC 7232 "HTTP: Conditional Requests"] this can be best accomplished by supporting the {ETag} header together with the {If-Match} or {If-None-Match} conditional header. The contents of an ETag: <entity-tag> header is either (a) a hash of the response body, (b) a hash of the last modified field of the entity, or (c) a version number or identifier of the entity version.

To expose conflicts between concurrent update operations via {PUT}, {POST}, or {PATCH}, the If-Match: <entity-tag> header can be used to force the server to check whether the version of the updated entity is conforming to the requested {entity-tag}. If no matching entity is found, the operation is supposed a to respond with status code {412} - precondition failed.

Beside other use cases, If-None-Match: * can be used in a similar way to expose conflicts in resource creation. If any matching entity is found, the operation is supposed a to respond with status code {412} - precondition failed.

The {ETag}, {If-Match}, and {If-None-Match} headers can be defined as follows in the API definition:

components:
  headers:
  - ETag:
      description: |
        The RFC 7232 ETag header field in a response provides the entity-tag of
        a selected resource. The entity-tag is an opaque identifier for versions
        and representations of the same resource over time, regardless whether
        multiple versions are valid at the same time. An entity-tag consists of
        an opaque quoted string, possibly prefixed by a weakness indicator (see
        [RFC 7232 Section 2.3](https://tools.ietf.org/html/rfc7232#section-2.3).

      type: string
      required: false
      example: W/"xy", "5", "5db68c06-1a68-11e9-8341-68f728c1ba70"

  - If-Match:
      description: |
        The RFC7232 If-Match header field in a request requires the server to
        only operate on the resource that matches at least one of the provided
        entity-tags. This allows clients express a precondition that prevent
        the method from being applied if there have been any changes to the
        resource (see [RFC 7232 Section
        3.1](https://tools.ietf.org/html/rfc7232#section-3.1).

      type: string
      required: false
      example: "5", "7da7a728-f910-11e6-942a-68f728c1ba70"

  - If-None-Match:
      description: |
        The RFC7232 If-None-Match header field in a request requires the server
        to only operate on the resource if it does not match any of the provided
        entity-tags. If the provided entity-tag is `*`, it is required that the
        resource does not exist at all (see [RFC 7232 Section
        3.2](https://tools.ietf.org/html/rfc7232#section-3.2).

      type: string
      required: false
      example: "7da7a728-f910-11e6-942a-68f728c1ba70", *

Please see [optimistic-locking] for a detailed discussion and options.

{MAY} consider to support Idempotency-Key header

When creating or updating resources it can be helpful or necessary to ensure a strong [idempotent] behavior comprising same responses, to prevent duplicate execution in case of retries after timeout and network outages. Generally, this can be achieved by sending a client specific unique request key – that is not part of the resource – via {Idempotency-Key} header.

The unique request key is stored temporarily, e.g. for 24 hours, together with the response and the request hash (optionally) of the first request in a key cache, regardless of whether it succeeded or failed. The service can now look up the unique request key in the key cache and serve the response from the key cache, instead of re-executing the request, to ensure [idempotent] behavior. Optionally, it can check the request hash for consistency before serving the response. If the key is not in the key store, the request is executed as usual and the response is stored in the key cache.

This allows clients to safely retry requests after timeouts, network outages, etc. while receive the same response multiple times. Note: The request retry in this context requires to send the exact same request, i.e. updates of the request that would change the result are off-limits. The request hash in the key cache can protection against this misbehavior. The service is recommended to reject such a request using status code {400}.

Important: To grant a reliable [idempotent] execution semantic, the resource and the key cache have to be updated with hard transaction semantics – considering all potential pitfalls of failures, timeouts, and concurrent requests in a distributed systems. This makes a correct implementation exceeding the local context very hard.

The {Idempotency-Key} header must be defined as follows, but you are free to choose your expiration time:

components:
  headers:
  - Idempotency-Key:
      description: |
        The idempotency key is a free identifier created by the client to
        identify a request. It is used by the service to identify subsequent
        retries of the same request and ensure idempotent behavior by sending
        the same response without executing the request a second time.

        Clients should be careful as any subsequent requests with the same key
        may return the same response without further check. Therefore, it is
        recommended to use an UUID version 4 (random) or any other random
        string with enough entropy to avoid collisions.

        Idempotency keys expire after 24 hours. Clients are responsible to stay
        within this limits, if they require idempotent behavior.

      type: string
      format: uuid
      required: false
      example: "7da7a728-f910-11e6-942a-68f728c1ba70"

Hint: The key cache is not intended as request log, and therefore should have a limited lifetime, else it could easily exceed the data resource in size.

Note: The {Idempotency-Key} header unlike other headers in this section is not standardized in an RFC. Our only reference are the usage in the Stripe API. However, we do not want to change the header name and semantic, and do not name it like the proprietry headers below. The header addresses a generic REST concern and is different from the specific proprietary headers.

{SHOULD} use only the specified proprietary headers

As a general rule, proprietary HTTP headers should be avoided. From a conceptual point of view, the business semantics and intent of an operation should always be expressed via the URLs path and query parameters, the method, and the content, but not via proprietary headers. Headers are typically used to implement protocol processing aspects, such as flow control, content negotiation, and authentication, and represent business agnostic request modifiers that provide generic context information ({RFC-7231}#section-5[RFC 7231]).

However, the exceptional usage of proprietary headers is still helpful when domain-specific generic context information…​

  1. needs to be passed end to end along the service call chain (even if not all called services use it as input for steering service behavior e.g. {X-Sales-Channel} header) and/or…​

  2. is provided by specific gateway components, for instance, our Fashion Shop API or Merchant API gateway.

Below, we explicitly define the list of proprietary header exceptions usable for all services for passing through generic context information of our fashion domain (use case 1).

Per convention, non standardized, proprietary header names are prefixed with X-. (Due to backward compatibility, we do not follow the Internet Engineering Task Force’s recommendation in {RFC-6648}[RFC 6648] to deprecate usage of X- headers.) Remember that HTTP header field names are not case-sensitive:

Header field name Type Description Header field value example

{X-Example}

String

For more information see above.

There once was a custom header

{MUST} propagate proprietary headers

All proprietary headers listed above are end-to-end headers [1]) defines two types of headers: end-to-end and hop-by-hop headers. End-to-end headers must be transmitted to the ultimate recipient of a request or response. Hop-by-hop headers, on the contrary, are meaningful for a single connection only.] and must be propagated to the services down the call chain. The header names and values must remain unchanged.

For example, the values of the custom headers like {X-Device-Type} can affect the results of queries by using device type information to influence recommendation results. Besides, the values of the custom headers can influence the results of the queries (e.g. the device type information influences the recommendation results).

Sometimes the value of a proprietary header will be used as part of the entity in a subsequent request. In such cases, the proprietary headers must still be propagated as headers with the subsequent request, despite the duplication of information.


1. HTTP/1.1 standard ({RFC-7230}#section-6.1[RFC 7230