Vercel
Routing Middleware for any framework on Vercel. Install, configure, verify.
The Vercel middleware is Routing Middleware:
a root middleware.ts that works with any framework on Vercel. It checks each
request before your pages run, and lets it through or returns a 403. No DNS
change.
Requirements
- A project deployed on Vercel.
- An API key.
- Already have a
middleware.ts? See Existing middleware.
Install
-
Install the package:
npm install @d3-inc/d3-edge-vercel-middleware -
Create
middleware.tsat the root of your project:import { createD3Middleware, config } from '@d3-inc/d3-edge-vercel-middleware'; export default createD3Middleware(); export { config }; -
Set
DNA_API_KEYin your Vercel project to the key from API keys (created here). Keep it in the secret store, not in code. -
Deploy, then verify the install.
Local development
Leave DNA_API_KEY unset locally and set it only in the deployed
environment. Unset, the middleware is a passthrough: it makes no network calls
and marks every response x-d3-edge: disabled, which confirms it loads.
Configuration
Set each option as an argument to createD3Middleware() or as an environment
variable. Arguments take precedence.
These need @d3-inc/d3-edge-vercel-middleware 0.8.0 or later.
| Option | Env var | Type | Default | Description |
|---|---|---|---|---|
apiKey | DNA_API_KEY | string | unset | Your organization's API key. Unset: passthrough; the middleware does nothing. |
enabled | DNA_ENABLED | boolean | true | false turns DNA off without removing the key. Any other value leaves it on. |
failMode | DNA_FAIL_MODE | "open" | "closed" | open | open: a request with no decision in time goes through. closed: it gets a 403. |
timeoutMs | DNA_TIMEOUT_MS | number (ms) | 2000 | Cutoff for a decision. |
paymentTimeoutMs | DNA_PAYMENT_TIMEOUT_MS | number (ms) | 10000 | Budget for a request that presents a payment, which settles inside the call. |
endpoint | DNA_ENDPOINT | string (URL) | https://edge-api.d3.com/v1/decision | Decision endpoint. Leave unset unless we give you another. |
debugLogRawPayload | DNA_DEBUG_LOG_RAW_PAYLOAD | boolean | false | Debug only: forwards every header but Authorization and Cookie for the request record. |
- With
failMode: open(the default), a request with no decision withintimeoutMsgoes through to your site. - With
failMode: closed, it gets a403instead. - A request that presents a payment and gets no decision within
paymentTimeoutMsgets a502, never the page. A refused payment is a402; see Pay with x402.
Earlier names
Installs set up with the earlier names keep working unchanged, and a new name
wins when both are set. With the earlier names, POLICY_WORKER_URL is still
what switches DNA on. DEBUG_LOG_RAW_PAYLOAD is read only while
POLICY_WORKER_URL is set.
| Earlier name | Now |
|---|---|
POLICY_ADAPTER_KEY, adapterKey | DNA_API_KEY, apiKey |
POLICY_SHARED_SECRET, sharedSecret | DNA_API_KEY, apiKey |
POLICY_WORKER_URL, policyWorkerUrl | DNA_ENDPOINT, endpoint, now optional |
POLICY_TIMEOUT_MS | DNA_TIMEOUT_MS |
POLICY_PAYMENT_TIMEOUT_MS | DNA_PAYMENT_TIMEOUT_MS |
FAIL_MODE | DNA_FAIL_MODE |
DEBUG_LOG_RAW_PAYLOAD | DNA_DEBUG_LOG_RAW_PAYLOAD |
To move over, update the package to 0.8.0 or later, then rename each variable
and drop POLICY_WORKER_URL. If you turned DNA off by leaving
POLICY_WORKER_URL unset, set DNA_ENABLED=false first: DNA_API_KEY
switches DNA on by itself.
Matcher
- The exported
configmatcher excludes Vercel's build assets:_next/static,_next/image,favicon.ico. - Files served from
public/are not excluded by default. - To check a signed upload, DNA reads the request body. Leave large-upload routes out of the matcher.
Response shape
Every response that passes through the middleware has an x-d3-edge header:
disabled, pass, block, fail-open, fail-closed, payment-required,
payment-redirect, or payment-stub. The payment-* values appear only on
access and pricing.
A blocked request gets a 403 with a JSON body:
{ "blocked": true, "ruleId": "…", "tier": "…", "identity": "…" }ruleId names the rule under Rules that stopped the request.
Existing middleware
Vercel runs one middleware.ts per project. Tell us
what yours does and we'll help you combine the two.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| Nothing in the log | DNA_API_KEY unset in the deployed environment (silent passthrough), or the key is rejected | Set the variable. Check Vercel logs for lines prefixed policy call rejected:, which include the HTTP status and reason. |
401 with unknown-key, expired, or revoked | The key is wrong, past its expiry, or revoked in the dashboard | Create a new key and update the secret. |
503 with not-configured | Your organization's keys are still being set up | If it persists more than a few minutes, contact us. |
| Middleware doesn't run | Not middleware.ts at the project root with a default export, or a custom matcher that misses the route | Fix the file location and export. If you replaced config, confirm your matcher covers the routes you're testing. |