MonetizationOS Docs
Proxies Reference

AWS Lambda Proxy

The AWS Lambda Proxy is an open source implementation of the MonetizationOS proxy that runs as a Node.js server on AWS Lambda Web Functions, behind an Amazon CloudFront distribution. To deploy it, follow the AWS Lambda Proxy guide.

View on GitHub

Environment variables

The proxy reads its variables from the Web Function revision's environment. infra/template.yaml sets them from stack parameters, with the defaults below.

VariableRequiredDescription
MONETIZATION_OS_SECRET_KEYYesYour MonetizationOS secret key. Used to authenticate Surface Decision API requests and to derive the environment prefix for custom endpoint routing.
ORIGIN_URLYesThe base URL of your origin server. All non-endpoint requests are proxied here.
SURFACE_SLUGNoThe MonetizationOS surface to evaluate for every HTML request. The template defaults to web.
AUTHENTICATED_USER_JWT_COOKIE_NAMENoCookie name containing the authenticated user's JWT. The template defaults to __session.
ANONYMOUS_SESSION_COOKIE_NAMENoCookie name for anonymous session identifiers. The template defaults to anon-session-id.
MONETIZATION_OS_HOSTNoOverride the MonetizationOS API host. Defaults to https://api.monetizationos.com.
MONETIZATION_OS_ENDPOINTS_PREFIXNoURL prefix for custom MOS endpoint routing. Defaults to /mos-endpoints/.
INJECT_SCRIPT_URLNoURL of a script appended to <head> after component transforms. Unset by default. Set it to https://assets.monetizationos.com/web-components-latest.js to use MonetizationOS web components.
ORIGIN_REQUEST_HEADERSNoJSON object of headers added to, or overriding, every request to the origin. The template defaults to {}.
SURFACE_DECISIONS_IGNORE_PATHSNoComma-separated regex patterns for paths that should skip surface decisions. No stack parameter.
SURFACE_DECISIONS_COOKIESNoComma-separated regex patterns for cookie names sent to the Surface Decision API as http.cookies. No stack parameter.
ORIGIN_VERIFY_SECRETNoShared secret that CloudFront sends as x-mos-origin-verify. The template sets it from the OriginVerifySecret parameter, which infra/deploy.sh generates on the first deploy. When unset, as in local development, the proxy skips the check.

CloudFront front door

The template puts a CloudFront distribution in front of the Web Function endpoint. Its default behavior uses the managed CachingDisabled cache policy and the AllViewerExceptHostHeader origin request policy, so requests reach the proxy with the viewer's headers, cookies, and query string.

Static paths

The viewer-request function sends paths ending in a StaticExtensions extension straight to ORIGIN_URL, without the proxy, because the proxy passes non-HTML responses through unchanged. It keeps the scheme, port, and path prefix of ORIGIN_URL, and turns off the Origin Access Control signature and the shared secret for these requests. Paths under MONETIZATION_OS_ENDPOINTS_PREFIX always go to the proxy, and setting ORIGIN_REQUEST_HEADERS turns static paths off.

Proxy headers

HeaderSet byPurpose
x-mos-viewer-hostViewer-request functionThe viewer's Host. Lambda only accepts its own domain as Host, so the proxy rebuilds the public URL from this.
x-mos-accept-encodingViewer-request functionThe viewer's Accept-Encoding, or identity when absent. Used to pick the response compression.
x-mos-origin-verifyCloudFront origin custom headerThe shared secret. Requests without a matching value get a 403.

The viewer-request function overwrites any viewer-sent value of the first two headers, and rejects requests without a Host with a 400. The proxy removes all three headers, the CloudFront Origin Access Control signature, and Lambda's own request headers before fetching from the origin.

With OriginAuth set to IamAuth, CloudFront also signs each request with Origin Access Control, and Lambda rejects unsigned callers before they reach the proxy.

Custom endpoint routing

Requests whose path begins with the MONETIZATION_OS_ENDPOINTS_PREFIX (default /mos-endpoints/) are forwarded directly to the MonetizationOS API as Endpoint Workflow requests. The remaining pipeline stages are skipped.

The environment prefix is extracted from MONETIZATION_OS_SECRET_KEY to target the correct environment:

/mos-endpoints/my-route → https://api.monetizationos.com/api/v1/envs/{env}/endpoints/my-route

CloudFront request context

The proxy calls the MonetizationOS API from an AWS Region, so it reads the viewer's location and network from CloudFront request headers instead. It sends them in an edge object in the Surface Decision API request body:

edge fieldCloudFront header
providerAlways cloudfront
requestIdX-Amz-Cf-Id
geo.countryCodeCloudFront-Viewer-Country
geo.regionCodeCloudFront-Viewer-Country-Region, an ISO 3166-2 subdivision
geo.regionCloudFront-Viewer-Country-Region-Name
geo.cityCloudFront-Viewer-City
geo.postalCodeCloudFront-Viewer-Postal-Code
geo.metroCodeCloudFront-Viewer-Metro-Code, US only
geo.latitude, geo.longitudeCloudFront-Viewer-Latitude, CloudFront-Viewer-Longitude
geo.timezoneCloudFront-Viewer-Time-Zone, an IANA time zone
network.asnCloudFront-Viewer-ASN
tls.versionCloudFront-Viewer-TLS, the protocol part only
tls.httpProtocolCloudFront-Viewer-Http-Version, in the form HTTP/2

Fields whose header is missing are left out. The edge object is sent whenever the request carries X-Amz-Cf-Id or CloudFront-Viewer-Country, even if CloudFront can't locate the viewer. JA3 and JA4 fingerprints aren't sent, because the AllViewerExceptHostHeader origin request policy can't forward them.

The viewer's IP address, from CloudFront-Viewer-Address, is sent as http.clientIP.

HTML transformation with lol-html

Component behaviors use lol-html, the streaming HTML rewriter behind Cloudflare's HTMLRewriter, built to WASM for Node.js. The proxy rewrites the body as it streams from the origin, and supports range replacement.

CSS selector limitations

  • Selectors using :last-child aren't supported and are silently skipped.
  • All other CSS selectors that lol-html supports work as expected. A component with a selector lol-html rejects is skipped.

Script injection

If INJECT_SCRIPT_URL is set and at least one component transform runs, the proxy appends the following to <head>:

<script src="{INJECT_SCRIPT_URL}" async defer></script>

Compression

CloudFront doesn't compress responses under CachingDisabled, so the proxy does. Static paths keep your origin's own compression. Text, JSON, JavaScript, XML, SVG, and WASM responses from the proxy are compressed with brotli, or gzip if the viewer doesn't accept brotli, and flushed per chunk so streamed HTML keeps streaming. Responses that are already encoded, partial, or marked Cache-Control: no-transform are passed through uncompressed.

Caching behavior

The proxy sets Cache-Control: no-store on all HTML responses during the link-rewriting stage. This prevents browser caching of personalized, per-user responses. Non-HTML responses passed through unmodified retain whatever cache headers the origin returned. The template's CloudFront behavior caches nothing, and neither Lambda nor Node.js fetch caches the proxy's requests to your origin. Put your origin behind its own CDN with cacheable responses, so the proxy's fetches, and the static paths CloudFront sends there, are served from that CDN rather than your servers.

Scaling and timeouts

The template sets the revision's TimeoutSeconds to 30 and MaxConcurrencyPerEnvironment to 32. Each execution environment runs one Node.js event loop, so throughput is bound by CPU per page. Tune the concurrency from your own load tests.

The endpoint has no MaxEnvironments cap by default, so Lambda adds environments as traffic grows. Lambda answers with 429 instead of scaling when the endpoint reaches MaxEnvironments, if you set one, or one of your account's Web Functions quotas in the Region. Each environment uses 2 vCPUs of the vCPU quota. Check your account's limits with aws lambda-web get-web-account-settings before a high-traffic launch, and see scaling behavior and Web Functions quotas for the current values.

The template's endpoint is HomeRegion, so the proxy runs in the stack's Region only. Deploy it in the Region nearest your origin.

Error handling

After the origin fetch, the shared pipeline falls back to the last safe response if a stage throws, so your site stays available if the MonetizationOS API is unreachable or a workflow errors. The Node.js server adds these responses of its own:

StatusWhen
400The viewer host or path can't form a valid URL
403ORIGIN_VERIFY_SECRET is set and x-mos-origin-verify is missing or doesn't match
502Any other error, for example the origin fetch failing

Each request is logged as one JSON line with its Lambda request ID, method, path without the query string, status, and duration.

On this page