Vercel
Zero-code source: point a Vercel Log Drain at MetrikStack and every request through Vercel's proxy becomes a pageview.
A Vercel Log Drain forwards
your deployment's logs to any HTTPS endpoint. Point one at
POST /v1/logs/vercel and MetrikStack turns the request logs into pageviews:
no code in your app, no script on the page, nothing to deploy.
Create a source of kind logs for the site in the dashboard to get an
ingest token (in_…).
Set up the drain
- Vercel dashboard → your team → Observability → Drains → Add Drain, choose Logs and a Custom endpoint.
- Endpoint:
https://api.example.com/v1/logs/vercel - Format:
NDJSON(aJSONarray is accepted too). - Sources:
StaticandFunction(lambda/edge). Build, firewall and external logs can stay off; they are discarded server-side anyway. - Environments:
Production(addPreviewif you want preview traffic counted in the same site). - Custom headers:
Authorization: Bearer in_…. - Save. Vercel immediately verifies the endpoint (see below).
If the drain UI you are using cannot set headers, put the token in the URL
instead; every /v1/logs/* endpoint accepts it as a query parameter:
https://api.example.com/v1/logs/vercel?token=in_...A URL carrying a token ends up in Vercel's project settings and in request logs; prefer the header when you have the choice, and rotate the token from the dashboard if it leaks.
Endpoint verification
Before it starts sending, Vercel probes the endpoint and requires the
response to carry an x-vercel-verify header with the value shown in the
Vercel UI. Set that value as vercel_verify in the source's config
(dashboard or API).
The header is attached to every response of /v1/logs/vercel, including
GET (which is what Vercel probes) and error responses, so a mistyped token
never blocks verification.
Signature checking (optional)
Vercel signs each delivery with x-vercel-signature, the hex SHA-1 HMAC of
the raw request body keyed with the drain's secret. Configure the secret as
vercel_secret in the source config, and every unsigned or mismatched
delivery is rejected with 403. With no secret configured the signature is
ignored and the bearer token is the only credential.
What becomes a pageview
An entry counts when all of the following hold:
sourceisstatic,lambdaoredge, and the entry has aproxyobject, that is, it describes a real request rather than build output or a function'sstdout;proxy.methodisGETorHEAD;proxy.pathis not an asset path (same extension and prefix rules as every other source; see Access logs);- the status is not a 3xx redirect.
Everything else is counted in the response's skipped figure, not rejected.
Fields map like this:
| Vercel | MetrikStack |
|---|---|
proxy.path | path + query |
proxy.host | hostname |
proxy.method | method |
proxy.statusCode | status_code |
proxy.userAgent[0] | user agent (bot classification, device, browser) |
proxy.referer | referrer |
proxy.clientIp | visitor hash + GeoIP country, then dropped |
proxy.vercelCache, proxy.cacheId | cache_status |
timestamp (ms) | event timestamp |
requestId | request_id |
proxy.region is the Vercel edge region (fra1), not the visitor's
location, so it is not stored as a region; country comes from the client IP
at ingest.
Drains and the request-id merge
The drain only ever sees requests that go through Vercel's proxy. Traffic served by another CDN in front of Vercel, or by a rewrite to an external origin, never reaches it.
Vercel's requestId is the platform's own id. It is not the
X-MetrikStack-Request-Id that the Node SDK sets on the response, and a
drain entry carries no application response headers, so the two ids cannot
be matched. In practice:
- Drain as the only source: every line is a pageview. This is the simplest and most accurate setup.
- Drain plus the tracker script or the Node SDK: there is no shared
request id, so merging falls back to the
2 s dedupe window on
(site, visitor, path). That is good enough for most sites, but a drain is usually better run as a standalone source, or as a secondary source for the cached/bot traffic the script never sees.
Response
{ "accepted": 42, "skipped": 118, "rejected": [] }skipped covers build logs, function output, assets, redirects and non-GET
requests. Batches arrive in near real time; Vercel retries failed
deliveries, so a short outage on our side does not lose data.