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.

Framework
Host

Settings for every host, with sources and dates.

1. Create your policy

In your project:

npx posterna init

It 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/express
import 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/next

Create 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:

HostSettingAlso
RailwayclientIpHeader: "x-real-ip"
VercelclientIpHeader: "x-vercel-forwarded-for"
Fly.iotrustedProxies: 2 (3 with Cloudflare in front)
RendertrustedProxies: 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 balancertrustedProxies: ["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.
nginxclientIpHeader: "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.
OtherclientIpHeader or trustedProxiesFind your host's setting in Hosts and proxies.

3. Deploy and check

  1. Set a temporary POSTERNA_DEBUG_TOKEN on the server, for example from openssl rand -hex 24, and deploy.

  2. From your own machine, run doctor with the same token:

    POSTERNA_DEBUG_TOKEN=<token> npx posterna doctor --url https://www.example.com
  3. Fix anything marked FAIL. Each failure names its fix.

  4. 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

Next

Counters, Sign-up routes, Switch to enforce, Policy file, Configuration.

Moved

The rest of the old install guide now has its own pages.