Docs menu
Quickstart
Posterna runs inside your Express or Next.js app. Three steps, about five minutes. It starts in monitor mode: it labels and counts every request and blocks nothing. On WordPress, use the WordPress plugin instead.
1. Create your policy
In your project:
npx posterna initIt writes agents.yaml in monitor mode and prints the code for step 2; it never edits your code. It can also save a site key from the dashboard, so counts show up there (Counters).
2. Add Posterna to your app
Express
npm install @posterna/expressimport express from "express";
import { posterna } from "@posterna/express";
const app = express();
app.use(
posterna({
clientIpHeader: "x-real-ip",
publicHost: "www.example.com", // your site's hostname
}),
);import express from "express";
import { posterna } from "@posterna/express";
const app = express();
app.use(
posterna({
clientIpHeader: "x-vercel-forwarded-for",
publicHost: "www.example.com", // your site's hostname
}),
);import express from "express";
import { posterna } from "@posterna/express";
const app = express();
app.use(
posterna({
trustedProxies: 2, // 3 with Cloudflare in front
publicHost: "www.example.com", // your site's hostname
}),
);import express from "express";
import { posterna } from "@posterna/express";
const app = express();
app.use(
posterna({
trustedProxies: 3, // check with posterna doctor; use 2 if it reports a forged address
publicHost: "www.example.com", // your site's hostname
}),
);3 comes from a Render employee's demonstration, not Render's documentation (Render).
import express from "express";
import { posterna } from "@posterna/express";
const app = express();
app.use(
posterna({
trustedProxies: ["10.0.0.0/16"], // replace with your load balancer subnets' CIDRs
publicHost: "www.example.com", // your site's hostname
}),
);The load balancer checks each target every 30 seconds by default, on /: give health checks their own path.
import express from "express";
import { posterna } from "@posterna/express";
const app = express();
app.use(
posterna({
clientIpHeader: "x-real-ip",
trustedProxies: ["127.0.0.1/32", "::1/128"],
publicHost: "www.example.com", // your site's hostname
}),
);Bind Node to 127.0.0.1 and add the proxy_set_header lines to nginx.
import express from "express";
import { posterna } from "@posterna/express";
const app = express();
app.use(
posterna({
// Set clientIpHeader or trustedProxies for your host:
// https://posterna.com/docs/hosts#settings-by-host
publicHost: "www.example.com", // your site's hostname
}),
);Find your host's setting in Hosts and proxies.
Add it before your routes.
Next.js
npm install @posterna/nextCreate proxy.ts next to your app folder (inside src/ if you use one):
import { withPosterna } from "@posterna/next";
export const proxy = withPosterna({
clientIpHeader: "x-real-ip",
publicHost: "www.example.com", // your site's hostname
});
export const config = {
matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"],
};import { withPosterna } from "@posterna/next";
export const proxy = withPosterna({
clientIpHeader: "x-vercel-forwarded-for",
publicHost: "www.example.com", // your site's hostname
});
export const config = {
matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"],
};import { withPosterna } from "@posterna/next";
export const proxy = withPosterna({
trustedProxies: 2, // 3 with Cloudflare in front
publicHost: "www.example.com", // your site's hostname
});
export const config = {
matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"],
};import { withPosterna } from "@posterna/next";
export const proxy = withPosterna({
trustedProxies: 3, // check with posterna doctor; use 2 if it reports a forged address
publicHost: "www.example.com", // your site's hostname
});
export const config = {
matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"],
};3 comes from a Render employee's demonstration, not Render's documentation (Render).
import { withPosterna } from "@posterna/next";
export const proxy = withPosterna({
trustedProxies: ["10.0.0.0/16"], // replace with your load balancer subnets' CIDRs
publicHost: "www.example.com", // your site's hostname
});
export const config = {
matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"],
};The load balancer checks each target every 30 seconds by default, on /: give health checks their own path.
import { withPosterna } from "@posterna/next";
export const proxy = withPosterna({
clientIpHeader: "x-real-ip",
trustedProxies: ["127.0.0.1/32", "::1/128"],
publicHost: "www.example.com", // your site's hostname
});
export const config = {
matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"],
};Bind Node to 127.0.0.1 and add the proxy_set_header lines to nginx.
import { withPosterna } from "@posterna/next";
export const proxy = withPosterna({
// Set clientIpHeader or trustedProxies for your host:
// https://posterna.com/docs/hosts#settings-by-host
publicHost: "www.example.com", // your site's hostname
});
export const config = {
matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"],
};Find your host's setting in Hosts and proxies.
Then wrap your Next.js config (why):
// next.config.ts
import { withPosternaConfig } from "@posterna/next/config";
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
// your existing settings
};
export default withPosternaConfig(nextConfig);On Next.js 15.5, use middleware.ts with runtime: "nodejs" instead (Next.js details).
The setting for your host, from Hosts and proxies:
| Host | Setting | Also |
|---|---|---|
| Railway | clientIpHeader: "x-real-ip" | |
| Vercel | clientIpHeader: "x-vercel-forwarded-for" | |
| Fly.io | trustedProxies: 2 (3 with Cloudflare in front) | |
| Render | trustedProxies: 3 (check with posterna doctor; use 2 if it reports a forged address) | 3 comes from a Render employee's demonstration, not Render's documentation (Render). |
| AWS load balancer | trustedProxies: ["10.0.0.0/16"] (replace with your load balancer subnets' CIDRs) | The load balancer checks each target every 30 seconds by default, on /: give health checks their own path. |
| nginx | clientIpHeader: "x-real-ip" and trustedProxies: ["127.0.0.1/32", "::1/128"] | Bind Node to 127.0.0.1 and add the proxy_set_header lines to nginx. |
| Other | clientIpHeader or trustedProxies | Find your host's setting in Hosts and proxies. |
3. Deploy and check
Set a temporary
POSTERNA_DEBUG_TOKENon the server, for example fromopenssl rand -hex 24, and deploy.From your own machine, run doctor with the same token:
POSTERNA_DEBUG_TOKEN=<token> npx posterna doctor --url https://www.example.comFix anything marked FAIL. Each failure names its fix.
Remove the token from the server.
Check with doctor explains each check.
That's it. Posterna is labeling every request.
Check these if they apply
- CloudFront in front of your app: a required origin request setting.
- Load balancer or platform health checks (AWS, Render, Fly.io, Kubernetes, Docker): give them their own path.
- Next.js with webhooks, large uploads or rewrites to another site: leave those routes out of the matcher.
- Next.js on the Edge runtime: works, with limits.
Next
Counters, Sign-up routes, Switch to enforce, Policy file, Configuration.
Moved
The rest of the old install guide now has its own pages.
- The install guide: Quickstart
- 1. Create agents.yaml: 1. Create your policy
- Next.js 16 (proxy.ts): Next.js details
- Next.js 15 (middleware.ts): Next.js details, Next.js 15
- The Edge runtime: Next.js details, the Edge runtime
- Keep health checks out of Posterna: Hosts and proxies, health checks
- 3. Tell Posterna about your proxies: Hosts and proxies
- Settings by host: Hosts and proxies, settings by host
- Amazon CloudFront in front of any origin: Hosts and proxies, CloudFront
- 4. Check the setup with doctor: Check with doctor
- Testing by hand: Check with doctor, testing by hand
- 5. Optional: send counters: Counters
- 6. Switch to enforce, safely: Switch to enforce
- Configuration reference: Configuration
- Install from a source build: Install from a source build