MonetizationOS Docs
Getting StartedIntegrate with MOS

Node.js Proxy

The MonetizationOS Node.js proxy is an open source integration that adds server-side monetization decisions to any HTML origin. It runs the same proxy core as the Cloudflare, Fastly, and Akamai proxies, hosted as a service you deploy yourself.

View on GitHub

What is the proxy core?

@monetizationos/proxy is a runtime-agnostic pipeline: a Fetch Request goes in, a transformed Response comes out. On Node.js, an HTTP service hosts that pipeline in front of your origin, on containers, virtual machines, or your own servers.

Why use the Node.js proxy?

The MonetizationOS Node.js proxy lets you evaluate and apply monetization behavior before HTML reaches the browser, on infrastructure you run yourself.

Installation and configuration

Prerequisites

1. Create the service

mkdir mos-node-proxy && cd mos-node-proxy
npm init -y
npm pkg set type=module
npm install @monetizationos/proxy @hono/node-server htmlrewriter

@hono/node-server serves the Fetch-based proxy on the Node.js HTTP server. The proxy applies component behaviors through htmlrewriter, which packages Cloudflare's HTMLRewriter for Node.js.

2. Create the server

Create server.js, which reads configuration from environment variables and serves the proxy:

server.js
import { serve } from "@hono/node-server";
import { MOSProxyBuilder } from "@monetizationos/proxy";
import { HTMLRewriter } from "htmlrewriter";

const htmlRewriter = {
    capabilities: { onEndTag: true, nthChild: true },
    create: () => new HTMLRewriter(),
};

const proxy = new MOSProxyBuilder()
    .withConfig({
        originUrl: process.env.ORIGIN_URL,
        surfaceSlug: process.env.SURFACE_SLUG,
        mosHost: process.env.MONETIZATION_OS_HOST || "https://api.monetizationos.com",
        mosSecretKey: process.env.MONETIZATION_OS_SECRET_KEY,
        anonymousSessionCookieName: process.env.ANONYMOUS_SESSION_COOKIE_NAME,
        authenticatedUserJwtCookieName: process.env.AUTHENTICATED_USER_JWT_COOKIE_NAME,
        injectScriptUrl: process.env.INJECT_SCRIPT_URL || undefined,
        surfaceDecisionsIgnorePaths: process.env.SURFACE_DECISIONS_IGNORE_PATHS,
    })
    .withHtmlRewriter(htmlRewriter)
    .build();

serve(
    {
        fetch: (request) => {
            if (new URL(request.url).pathname === "/healthz") {
                return new Response("ok");
            }
            return proxy.handle(request);
        },
        port: Number(process.env.PORT) || 3000,
        overrideGlobalObjects: false,
    },
    (info) => {
        console.log(`MonetizationOS proxy listening on http://localhost:${info.port}`);
    },
);

htmlRewriter is the adapter the proxy core uses for HTML transforms. create() returns a rewriter for each response, and capabilities tells the core which rewriter features it can use: onEndTag for replacing a range of elements, nthChild for :nth-child() selectors. Any package that implements Cloudflare's HTMLRewriter API can take the place of htmlrewriter. overrideGlobalObjects: false keeps the native Request and Response objects the proxy core expects. The /healthz route answers load balancer health checks, which the AWS ECS Fargate guide relies on.

3. Configure environment variables

VariableDescription
MONETIZATION_OS_SECRET_KEYYour MonetizationOS secret key. Get it from environment settings.
ORIGIN_URLThe base URL of your origin server.
SURFACE_SLUGThe MonetizationOS surface to evaluate for every HTML request.
AUTHENTICATED_USER_JWT_COOKIE_NAMECookie name containing the authenticated user's JWT.
ANONYMOUS_SESSION_COOKIE_NAMECookie name for anonymous session identifiers.
SURFACE_DECISIONS_IGNORE_PATHSComma-separated regex patterns for paths that should skip surface decisions (optional)
MONETIZATION_OS_HOSTMonetizationOS API host. Defaults to https://api.monetizationos.com (optional)
INJECT_SCRIPT_URLURL of the MonetizationOS web components script to inject when component transforms run (optional)
PORTPort the HTTP server listens on. Defaults to 3000 (optional)

For further configuration fields, such as surfaceDecisionsCookies and originRequestHeaders, see the project README.

4. Run locally

Start the service with your configuration:

ORIGIN_URL=https://news.example.com \
SURFACE_SLUG=web \
MONETIZATION_OS_SECRET_KEY=<YOUR_SECRET_KEY> \
AUTHENTICATED_USER_JWT_COOKIE_NAME=__session \
ANONYMOUS_SESSION_COOKIE_NAME=anon-session-id \
node server.js

The terminal prints:

MonetizationOS proxy listening on http://localhost:3000

Request a page through the proxy:

curl -i http://localhost:3000/

The response is your origin's HTML. Notice the Set-Cookie header on the first response: the proxy created an anonymous session identifier for the visitor.

5. Containerize

Create a Dockerfile that installs the runtime dependencies and runs the service:

Dockerfile
FROM node:22-alpine

WORKDIR /app
ENV NODE_ENV=production

COPY package*.json ./
RUN npm ci --omit=dev

COPY server.js ./

USER node
EXPOSE 3000

CMD ["node", "server.js"]

Build and run the image:

docker build -t mos-node-proxy .
docker run --rm -p 3000:3000 \
  -e ORIGIN_URL=https://news.example.com \
  -e SURFACE_SLUG=web \
  -e MONETIZATION_OS_SECRET_KEY=<YOUR_SECRET_KEY> \
  -e AUTHENTICATED_USER_JWT_COOKIE_NAME=__session \
  -e ANONYMOUS_SESSION_COOKIE_NAME=anon-session-id \
  mos-node-proxy

The container prints the same listening line, and curl -i http://localhost:3000/ returns your origin's HTML.

For canonical setup details and updates, see the project README.

Next steps

On this page