Navigated to /docs/core/security

Security

Keep request data isolated, validate mutation origins, protect secrets, and harden production SSR boundaries.

Understand the secure defaults

Tavo.js escapes TSX text and attributes, rejects unsafe URL protocols, validates action origins for unsafe methods, applies baseline SSR headers, blocks external redirects by default, and disables remote image optimization until hosts are allowlisted.

  • Keep raw HTML escape hatches free of user input.

  • Set trustedHosts and canonicalOrigin behind a reverse proxy.

  • Tune maxRequestBodyBytes for mutation endpoints.

  • Add a deployment-specific Content Security Policy at the edge or adapter.

Keep authentication request-scoped

Read cookies and sessions inside server middleware, loaders, and actions. Return only safe user fields to the rendered page. The memory store below makes the example runnable in local development; replace it with a shared production session store before deploying multiple processes. Global stores, services, and module variables may be shared between concurrent SSR requests.

TS
tsimport "@tavojs/core/server-only";
import { createSessionStorage } from "@tavojs/core/server";

const secret = process.env.SESSION_SECRET;
if (!secret) throw new Error("SESSION_SECRET is required");

export const sessions = createSessionStorage<{ userId?: string }>({
  cookie: {
    name: "__session",
    secrets: [secret],
    maxAge: 60 * 60 * 24 * 7,
    sameSite: "lax"
  }
});
TS
tsimport { defineServerLoader } from "@tavojs/core/router";

export const load = defineServerLoader(async ({ request }) => {
  const { sessions } = await import("../../server/sessions");
  const session = await sessions.getSession(request);
  const userId = session.get("userId");
  return {
    user: userId ? { id: userId, name: "Signed-in developer" } : null
  };
});

Review production boundaries

Security is a deployment property as well as a framework property. Recheck proxy headers, host validation, cookies, CSP, and secret injection in the actual hosting environment.

  • Store signing keys and API secrets outside source control.

  • Use server-only modules for databases, sessions, and private clients.

  • Authenticate webhooks independently before disabling origin validation.

  • Protect the monitor endpoint with TAVO_MONITOR_TOKEN.

  • Allow only exact remote image hosts and paths that the product needs.

Framework security defaults

  • TSX text and attribute values are escaped. Unsafe attribute names and javascript-style URL protocols are rejected.

  • External redirects are blocked by default. Validate any target before enabling allowExternalRedirects.

  • Unsafe route actions and plugin handlers validate Origin by default. Node handlers also require a local or trusted inbound host.

  • SSR HTML and optimized image responses include nosniff, strict-origin-when-cross-origin, a restrictive camera/microphone/geolocation policy, and SAMEORIGIN framing.

  • Node mutation bodies are limited to 10 MiB by default. Tune maxRequestBodyBytes or use direct-to-storage uploads for large files.

  • Remote image optimization is disabled until hosts are explicitly allowlisted. Private hosts, unsafe redirects, path escapes, and oversized inputs are rejected.

Treat hydration state as browser-readable data

Successful page loader results, layout loader results, Store snapshots selected for hydration, and plugin hydration contributions are serialized into the HTML response so the browser can resume the same application state. Escaping protects the document from script injection; it does not make those values private.

  • Return display DTOs with only the fields that the rendered interface needs.

  • Never return session IDs, access tokens, signing secrets, database records with private columns, or authorization-only policy details.

  • Keep private values in server loaders, actions, middleware, sessions, or request-scoped plugin resources and derive a separate browser-safe result.

  • Inspect the production HTML and __TAVO_STATE__ payload during security review; do not rely only on what is visibly rendered.

Server-only module boundaries

Place databases, session storage, secrets, and private clients under src/server or import the server-only marker. Use defineServerOnly to add a runtime assertion around an exported function.

  • The server-only marker has no runtime exports; the build guard uses the import boundary to keep the module out of client bundles.

  • defineServerOnly throws if the wrapped function is called in a browser.

  • A shared route module can dynamically import a server module from an action, server loader, or server middleware.

TS
tsimport "@tavojs/core/server-only";
import { defineServerOnly } from "@tavojs/core/server";

export const getPrivateClient = defineServerOnly(() => createPrivateClient({
  token: process.env.PRIVATE_API_TOKEN
}));

Safe mutation and authentication order

  • Enforce the expected content type and request body limit.

  • Validate the payload shape, then authenticate and authorize the current request.

  • Keep origin validation enabled for browser mutations. Authenticate webhooks with a signature before opting out.

  • Use idempotency keys or a transaction for retries. AbortSignal cancellation cannot undo a committed side effect.

  • Return only safe fields. Never serialize access tokens, session IDs, or private service responses into route data.

  • Keep request identity out of global stores, plugin runtime stores or capabilities, application services, and module variables because server processes handle concurrent requests.

Content Security Policy and raw content

Tavo.js does not set one universal Content Security Policy because allowed scripts, styles, images, fonts, and analytics differ by application. Add a policy at the deployment edge or adapter and test the production SSR response.

TEXT
textContent-Security-Policy:
  default-src 'self';
  base-uri 'self';
  object-src 'none';
  frame-ancestors 'self';
  img-src 'self' data:;
  script-src 'self' 'nonce-{nonce}';
  style-src 'self';
  font-src 'self'

Look up exact public types

Follow linked API names to their canonical TypeScript declarations and package boundaries.