Node runtime and production security
Configure the generated Node server, handler, origin and host checks, document boundary, static cache, and remote image optimizer.
Configure the stable server boundary
Place under ssr in the one root tavo.config.ts. Tavo.js supplies route modules and top-level plugins to generated builds; applications configure the remaining handler behavior. Document fields follow , and image fields follow .
RenderDocumentOptionshas title andunsafeHeadHtml. It has no rawdocument.headstring alias.Prefer route head exports and escaped TSX metadata.
unsafeHeadHtmlis unescaped and must never contain user-controlled content.canonicalOriginmust contain only an HTTP(S) origin: no credentials, path, query, or hash.maxRequestBodyBytesdefaults to 10MiBfor the Node handler.Cookie or Authorization requests bypass static response caching.
tsimport { defineConfig } from "@tavojs/core/config";
const reviewedBootstrapHtml =
'<meta name="application-name" content="Acme">';
export default defineConfig({
ssr: {
canonicalOrigin: "https://app.example.com",
trustedHosts: ["app.example.com"],
maxRequestBodyBytes: 10 * 1024 * 1024,
stream: true,
document: {
lang: "en",
title: "Acme",
unsafeHeadHtml: reviewedBootstrapHtml
},
images: {
allowRemote: true,
remotePatterns: [{
protocol: "https:",
hostname: "images.example.com",
pathname: "/media/**"
}]
}
}
});Know the production HTTP behavior
applies this contract to the generated Node server. Low-level document and page render functions remain available when a platform needs a custom server integration.
| API / contract | Type / boundary | Default / result | Behavior |
|---|---|---|---|
GET / HEAD | page request | render route | Resolves the route, plugins, images, static response cache, and optional streaming document. |
unsafe method + action | mutation | action response | Validates host/origin and optional content type before running the route action. |
unsafe method + plugin endpoint | mutation | plugin response | Runs matching server middleware and the most-specific endpoint with request-scoped disposal. |
non-page method without handler | HTTP failure | 405 | Returns Allow: GET, HEAD and baseline security headers. |
body over limit | HTTP failure | 413 | Rejects before an action or endpoint receives the body. |
uncaught handler failure | HTTP failure | generic 500 | Contains the exception and avoids returning private error details. |
client disconnect | cancellation | AbortSignal | Aborts request-owned work and cancels an active streaming reader. |
HTML, actions, plugin responses, images, and generated static assets receive baseline nosniff, referrer, permissions, and framing headers where applicable.
Tavo.jsdoes not invent a universal Content Security Policy. Add a deployment-specific policy and pass one request nonce through intentional inline content.The handler exposes
invalidateCache(tags) andclearCache() for rendered and resolved cache invalidation.
tsimport { createServer } from "node:http";
import { createNodeRequestHandler } from "@tavojs/core/server";
import * as modules from "virtual:tavo-pages";
const handleRequest = createNodeRequestHandler({
modules,
trustedHosts: ["example.com"],
stream: true,
});
createServer(handleRequest).listen(3000);Apply the exact origin and Host policy
Origin validation applies to unsafe methods when validateOrigin is not false. A present Origin must equal the normalized request URL origin. Missing Origin is accepted by the Fetch handler; the Node handler still requires a local or configured trusted inbound host.
| API / contract | Type / boundary | Default / result | Behavior |
|---|---|---|---|
Fetch + matching Origin | unsafe action / endpoint | accepted | Origin exactly matches new URL(request.url).origin. |
Fetch + mismatched Origin | unsafe action / endpoint | 403 | Rejected before application mutation code runs. |
Fetch + missing Origin | unsafe action / endpoint | accepted | Support for non-browser clients; authentication and authorization remain required. |
Node + untrusted Host | unsafe action / endpoint | 403 | Rejected even when Origin is missing or agrees with the forged Host. |
Node + local Host | localhost / loopback | trusted | Localhost, 127.0.0.1, and loopback IPv6 are implicit. |
Node + trustedHosts | exact host or hostname | accepted | A configured hostname also matches that hostname with an inbound port. |
Node + canonicalOrigin | reverse proxy | public URL origin | Constructs request URLs from the public origin and adds its host and hostname to the trusted set. |
validateOrigin: false | explicit opt-out | no origin check | Reserve for independently authenticated integrations such as signature-verified webhooks. |
Allowlist remote images narrowly
Configure remote loading through . The optional resolveHostname hook is a platform adapter with the exact signature shown below.
| API / contract | Type / boundary | Default / result | Behavior |
|---|---|---|---|
allowRemote | boolean | false | Remote sources remain disabled until explicitly enabled. |
remotePatterns | string | { protocol, hostname, port, pathname }[] | [] | Allows only matching remote origins and paths. Prefer HTTPS and the narrowest pathname. |
resolveHostname | (hostname: string) => Promise<Array<{ address: string }>> | Node DNS | Advanced test/platform hook; every returned address must pass public-network validation. |
allowInsecureRemote | boolean | false | Relaxes HTTP/private-network protections. Use only inside a controlled network boundary. |
timeoutMs / maxBytes | number | 5000 / 10 MiB | Bounds remote fetch time and source bytes. |
maxConcurrentTransforms | number | 4 | Limits active image transformations. |
maxPendingTransforms | number | 64 | Excess queued transformations receive 503. |
The optimizer rejects private hostnames and private DNS results, and checks redirected locations again.
Local absolute paths must remain inside
publicDir.Set the
Imagecomponent's unoptimized prop when the source should bypass the optimizer.Install the optional sharp peer only on servers that perform transformations.
Operate the generated Node server
| API / contract | Type / boundary | Default / result | Behavior |
|---|---|---|---|
NODE_ENV | environment | production | Selects mode-specific server env loading before the built entry is imported. |
HOST | bind address | 127.0.0.1 | Use 0.0.0.0 only when the container or platform must accept external connections. |
PORT | number | 4174 | HTTP listen port. |
TAVO_MONITOR_TOKEN | secret | monitor disabled | Enables /_tavo/monitor with an exact Authorization: Bearer header. Unauthorized requests return 404. |
assets/* | fingerprinted static assets | 1 year immutable | Generated hashed assets receive long-lived caching. |
other client files | static files | no-cache | HTML and non-fingerprinted files are revalidated. |
Deploy .
tavo/build/client for fully client/static applications or run.tavo/build/server/start.mjsfor request-time behavior.Plain
tavopreview delegates to Vite preview.tavopreview--ssrchecks for missing or stale output and runstavobuild first when required.Protect the external TLS, proxy header, CSP, secret injection, health-check, and process supervision boundaries on the hosting platform.
bashnpx tavo build
HOST=0.0.0.0 PORT=4174 node .tavo/build/server/start.mjsVerify the deployed boundary
Exercise one SSR GET, one client navigation, one action, one plugin endpoint, one error response, and one remote image policy decision.
Verify the public Origin observed behind the real reverse proxy.
Confirm an untrusted Host and mismatched Origin receive 403 before mutation code runs.
Confirm the monitor endpoint is hidden without the Bearer token.
bashnpx tavo check
npx tavo build --report-json
npx tavo preview --ssr
npx tavo monitor --url http://127.0.0.1:4174 --token "$TAVO_MONITOR_TOKEN" --onceLook up exact public types
Follow linked API names to their canonical TypeScript declarations and package boundaries.