Skip to content
10 min read

Log Drains Reference

Learn about Log Drains types and sources.
Table of Contents
Choose a framework to optimize documentation to:

Log Drains allow you to collect logs from your deployments. To enable Log Drains, you must provide a destination URL to send the logs to. This URL is provided by the provider that ingests the Log Drains.

Vercel sends logs to destination URLs over HTTPS, HTTP, TLS, or TCP every time logs are generated.

Vercel supports three different types of Log Drains:

When you choose the json type, the URL receives a HTTPS or HTTP POST request with a JSON array on the POST body.

If the response of the request returns an HTTP statusCode with a value of -1, that means there was no response returned and the lambda crashed. In the same response, if the value of proxy.statusCode is returned with -1, that means the revalidation occurred in the background.

The logs are buffered and submitted as batches with the following formats:

    "id": <identifier>,
    "message": <text>,
    "timestamp": <timestamp>,
    "type": <"stdout" or "stderr">,
    "source": <"build", "static", "external", or "lambda">,
    "projectId": <identifier of project>,
    "deploymentId": <identifier of deployment>,
    "buildId": <identifier of build>,
    "host": <hostname>,
    "entrypoint": <entrypoint>
    "id": <identifier>,
    "message": <text>,
    "timestamp": <timestamp>,
    "requestId": <identifier of request only on runtime logs>,
    "statusCode": <HTTP status code of request only on runtime logs>,
    "source": <"build", "static", "external", or "lambda">,
    "projectId": <identifier of project>,
    "deploymentId": <identifier of deployment>,
    "buildId": <identifier of build only on build logs>,
    "destination": <origin of external content only on external logs>,
    "host": <hostname>,
    "path": <path>,
    "proxy": {
      "timestamp": <timestamp of proxy request>,
      "method": <method of request>,
      "scheme": <protocol of request>,
      "host": <hostname>,
      "path": <path of proxy request>,
      "userAgent": <user agent>,
      "referer": <referer>,
      "statusCode": <HTTP status code of proxy request>,
      "clientIp": <client IP>,
      "region": <region request is processed>,
      "cacheId": <original request id when request is served from cache>,
      "vercelCache": <the X-Vercel-Cache value sent to the browser>

The requests are posted with a x-vercel-signature header which contains a hash signature you can use to validate the request body. See the Securing your Log Drains section to learn how to verify requests.

When you choose the ndjson type, the URL receives a HTTPS or HTTP POST request with JSON objects delimited by newline (\\n) on the POST body. See for more information on the structure.

Each request receives HTTP headers including x-vercel-signature.

The following are two example POST bodies:

  "id": "1573817187330377061717300000",
  "message": "done",
  "timestamp": 1573817187330,
  "type": "stdout",
  "source": "build",
  "projectId": "abcdefgdufoJxB6b9b1fEqr1jUtFkyavUURbnDCFCnZxgs",
  "deploymentId": "dpl_233NRGRjVZX1caZrXWtz5g1TAksD",
  "buildId": "bld_cotnkcr76",
  "host": "*",
  "entrypoint": "api/index.js"
  "id": "1573817250283254651097202070",
  "message": "START RequestId: 643af4e3-975a-4cc7-9e7a-1eda11539d90 Version: $LATEST\\n2019-11-15T11:27:30.721Z\\t643af4e3-975a-4cc7-9e7a-1eda11539d90\\tINFO\\thello\\nEND RequestId: 643af4e3-975a-4cc7-9e7a-1eda11539d90\\nREPORT RequestId: 643af4e3-975a-4cc7-9e7a-1eda11539d90\\tDuration: 16.76 ms\\tBilled Duration: 100 ms\\tMemory Size: 1024 MB\\tMax Memory Used: 78 MB\\tInit Duration: 186.49 ms\\t\\n",
  "timestamp": 1573817250283,
  "source": "lambda",
  "requestId": "894xj-1573817250172-7847d20a4939",
  "statusCode": 200,
  "proxy": {
    "timestamp": 1573817250172,
    "path": "/api",
    "userAgent": [
      "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36"
    "referer": "*",
    "method": "GET",
    "scheme": "https",
    "host": "*",
    "statusCode": 200,
    "clientIp": "",
    "region": "sfo1"
  "projectId": "abcdefgdufoJxB6b9b1fEqr1jUtFkyavUURbnDCFCnZxgs",
  "deploymentId": "dpl_233NRGRjVZX1caZrXWtz5g1TAksD",
  "host": "*",
  "path": "api/index.js"
Deprecated: Syslog is not supported in configurable log drains. This is a deprecated feature for integrations.

When you choose the syslog type, the URL is connected with TLS or TCP. Log Drain messages are formatted according to RFC5424 framed using octet counting defined in RFC6587.

Syslog messages resemble the following:

425 <142>1 2019-11-15T11:42:22.562Z * now proxy - [proxy@54735 requestId="q8k4w-1573818142562-9adfb40ce9d4" statusCode="200" method="GET" path="/api" userAgent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36" referer="*" clientIp="" region="sfo1" signature="b847f4dd531d0b41094fb4b38fd62bde0b0e29a5"]587 <150>1 2019-11-15T11:42:22.833Z * now lambda - [lambda@54735 requestId="q8k4w-1573818142562-9adfb40ce9d4" statusCode="200" path="api/index.js" signature="0900101157dac2a2e555524c2f8d61229b15307d"] BOMSTART RequestId: ec00309f-4514-4128-8b8a-9a0e74900283 Version: $LATEST
END RequestId: ec00309f-4514-4128-8b8a-9a0e74900283
REPORT RequestId: ec00309f-4514-4128-8b8a-9a0e74900283\\tDuration: 20.08 ms\\tBilled Duration: 100 ms Memory Size: 1024 MB\\tMax Memory Used: 77 MB\\tInit Duration: 157.97 ms

Similar to JSON and NDJSON drains, a syslog message contains a hash signature for verifying messages on the signature key of structured data. On syslog drains, the signature is computed using an OAuth2 secret and the MSG section of the syslog format.

All drains support transport-level encryption using HTTPS or TLS protocols, and it is recommended to use them on production and use others only for development and testing.

When your server starts receiving payloads, it could be a third party sending log messages to your server if they know the URL. Therefore, it is recommended to use HTTP Basic Authentication, or verify messages are sent from Vercel using an OAuth2 secret and hash signature.

For example, if you have a basic HTTP server subscribing to Log Drains, the payload can be validated like so:

Next.js (/app)
Next.js (/pages)
Other frameworks
import crypto from 'crypto';
export async function GET(request: Request) {
  const { INTEGRATION_SECRET } = process.env;
  if (typeof INTEGRATION_SECRET != 'string') {
    throw new Error('No integration secret found');
  const rawBody = await request.text();
  const rawBodyBuffer = Buffer.from(rawBody, 'utf-8');
  const bodySignature = sha1(rawBodyBuffer, INTEGRATION_SECRET);
  if (bodySignature !== request.headers.get('x-vercel-signature')) {
    return Response.json({
      code: 'invalid_signature',
      error: "signature didn't match",
async function sha1(data: Buffer, secret: string): string {
  return crypto.createHmac('sha1', secret).update(data).digest('hex');

You can compute the signature using an HMAC hexdigest from the secret token of the OAuth2 app and request body, then compare it with the value of the x-vercel-signature header to validate the payload.

In order to configure the logs you want to receive, you can provide one or more sources when creating a log drain:

Requests to static assets like HTML and CSS files
Output from Vercel Functions like API Routes
Output from Edge Functions like Middleware
Output from the Build Step
External rewrites to a different domain


  "sources": ["static", "lambda", "edge"]

While this parameter is optional, providing at least one log source is highly recommended. If you do not provide any log sources, the log drain will default to edge, lambda, static, and external.

Last updated on February 23, 2023