Request verification
Using app events and identities enables you to easily build backends for your app. However, configuring your app backend to listen to app events and requests from the app frontend also exposes it the public internet, allowing anyone to make requests to it. For security reasons, we recommend limiting requests to only those coming from Contentful and other trusted sources. Request verification allows you to easily verify that a request or event has come from Contentful by signing requests with a signing secret.
This article will walk you through establishing a signing secret, using this secret to sign requests, and finally verifying those requests in the app backend.
How it works
Using a signing secret, a signature can be generated for any request. This signature and some additional metadata is then added to the headers of the request before it is sent. The headers look like this:
When an app backend receives a request, it can compute a signature in the same way. If the secret is the same, the signatures will match too, thus verifying that the sender knows the secret. For this reason, it is also important to not share the signing secret with any third party.
Establishing a signing secret
A signing secret can be generated in the security tab of the app definition page. Click on Enable request verification in this tab, and you will be prompted to generate a new secret or submit your own.

After generating a secret, be sure to immediately copy it to somewhere safe, as it can not be revealed again after the dialog disappears.

You can also manage secrets directly using the Content Management API (CMA). Visit the API documentation for details on how to manage the signing secret by using the API.
Signing requests
App Events
Once a signing secret has been created, all app events for the respective app will be automatically signed using it. You can directly proceed to verifying them on your backend.
App Frontend
Once a secret has been created, signing requests made by an app frontend is done by calling a method with the cma adapter.
Specifically, cma.appSignedRequest.create returns the signature headers that you can put onto your request in order for it to be recognized as signed, like so:
For more details, visit the App SDK documentation.
Verifying requests on the backend
Now that requests from your app contain the additional signature headers, your app backend can start to enforce signed requests. The following is an example server in Node.js using the Express framework:
This example uses node-apps-toolkit, which contains tools to create app backends in Node.js.
The verifyRequest function will try to sign the contents of the incoming request and then compare them to the meta-information contained in the signing headers.
The function will throw an error if the incoming request is shaped incorrectly or is older than the specified time-to-live (TTL). You can pass the TTL in seconds as an additional argument, or disable it by passing 0. If nothing is passed, the TTL will be 30 seconds.
The documentation for node-apps-toolkit contains more detailed information on the signing and verification process.
verifyRequest function yourself. There is a pseudocode implementation of the verification algorithm at the bottom of this page which can be used as a reference for your implementation.Key rotation
Changing your secret while you are already enforcing secure requests requires some additional steps. This is because changing the secret on either side will cause verification to fail until the other side has been updated too. We can get around this by verifying against multiple secrets simultaneously in the app backend. The process looks like this:
- Generate a new secret locally
- Verify requests against both the old and the new secret in your app backend
- Submit the new secret to Contentful using the web app or the API, causing further requests to be signed with the new key
- Remove support for the old key from your app backend
The following sections will go into detail on how to generate a secret and how to add support for multiple secrets in an app backend.
Creating a secret
There are two constraints on the secret:
- It must be exactly 64 characters long.
- It must match this regular expression:
/^[0-9a-zA-Z+/=_-]+$/. This also means secrets using the hex or base64 character set are allowed.
To make full use of the security provided by secure requests, we recommend using cyptographic random number generators with high entropy to generate a secret. Here are some examples for how to do this using different languages and tools. All examples can be run on the command line:
Verify requests against multiple secrets
To properly test against multiple secrets, an attempt to verify the incoming request has to be made separately for each secret, as the request should only be rejected if none of the secrets can verify the request. The following is an example in Node.js using the Express framework:
Using additional headers from verified requests
When request verification is enabled, additional headers are included in requests that contain metadata about the request. These headers are used as part of the request verification process - but can also be used in your application if you need this information. The headers are x-contentful-space-id, x-contentful-environment-id and x-contentful-user-id.
The following is an example in Node.js using the Express framework:
Submitting a locally generated secret
You can submit your own secret on the same page where you originally enabled request verification. Click on the Update button and paste in your secret, as shown in the image below.

After confirming by clicking on Update again, Contentful will start signing requests with your submitted secret.
Verifying a signature
A working JavaScript implementation of signature verification can be found in the node-apps-toolkit. Additional working examples in C++, C#, Elixir, Go, Java, Kotlin, PHP, Python, Ruby, and Rust can be found in the request-verification-examples repository. If you need to verify a request in another programming language, you can use this pseudocode example of how signed requests can be verified as the basis for your implementation: