ohmyhost init --dry-run --json in the Next.js project. Keep an exact package-manager version and one matching lockfile; resolve the concrete inspection output before planning a deployment.
Supported versions and build
Next.js requires stable16.4.1 or later. Every Next.js 15 release, Next.js 16.4.0 and older, and prereleases are refused. The security floor is enforced on new plans and builds in Dev and Prod; existing deployments need a rebuild to adopt an updated application dependency.
The version resolved by the committed package-lock.json or pnpm-lock.yaml decides; otherwise the exact version or ^/~ range in package.json does, and a range spanning the floor stays pending until the build checks the installed version. There is no upper version bound in that security check, but a newer release must still build: OpenNext refuses a Next.js major it does not support yet. Pin exact react and react-dom versions and commit the matching package-manager lockfile.
Keep scripts.build as next build. The service supplies OpenNext 1.20.9 and adds --webpack; next build --webpack is also accepted. A committed open-next.config.ts is ignored because the service writes its own OpenNext configuration. Use one default-exported next.config.js, .mjs or .ts; do not set output: standalone, output: export or a non-root basePath or assetPrefix.
The Webpack build ignores Turbopack rules. create-next-app 16.4 with Tailwind CSS, its --yes default, loads Tailwind only through the Turbopack rule @tailwindcss/turbopack, so planning refuses it as next_tailwind_turbopack_only until the application root has a PostCSS configuration that Webpack reads. Add postcss.config.mjs with export default { plugins: { "@tailwindcss/postcss": {} } }, remove the Turbopack rule, and use the package manager to add @tailwindcss/postcss at the same version as tailwindcss and remove @tailwindcss/turbopack, so the lockfile records both. An app created with --no-tailwind needs no change.
The service-owned Next.js bundle uses keep_names: false to prevent injected name helpers from breaking browser scripts serialized by libraries. This setting is supplied by the platform; no customer Wrangler override is needed.
Build-time static pages and cache limits
Pages and route responses prerendered during the build use an immutable cache carried with that deployment. Ordinary request-time rendering and route handlers remain available. A new deployment refreshes the cache. Time-based ISR and on-demand revalidation are unsupported; keep existing static pages when addressing a packaging error.Cache Components
Next.js 16 Cache Components (cacheComponents: true, also with partialPrefetching: true, the create-next-app 16.4 default) build and run. A page’s prerendered static shell comes from the deployment’s cache and its Suspense holes render for each request. "use cache" and the next/cache functions cacheLife, cacheTag, io, navigation, prefetch, refresh and unstable_noStore (imported or re-exported by name) are admitted.
- The cache is read-only: request-time
"use cache"values are never stored, so a later request computes them again. A cached function must depend on request data only through its arguments. - A
"use cache"value in a static shell would stay frozen until the next deployment, and every built-incacheLifeprofile revalidates. The build refuses it unless it usescacheLife({ revalidate: Infinity, expire: Infinity }); otherwise call the cached function inside a Suspense boundary after reading request data, for exampleawait connection(). revalidateTag,revalidatePath,updateTag,unstable_expireTag,unstable_expirePath,unstable_cacheand othernext/cacheuses stay refused.- Each dynamic page needs a static shell: read
paramsinside a Suspense boundary, withoutensureStaticand without a root layout inside a dynamic segment. WithpartialPrefetching: true, dynamic pages must not usegenerateStaticParams.
ohmyho.st packaging: line that names the fix; apply it, then deploy the new commit.
The private cache defaults to 32 MiB. With CLI/MCP 0.1.28 or later, add build.ssg_cache_max_mib to ohmyhost.yaml to select an integer from 1 to 64. Keep your existing build settings, for example:
- Ordinary public assets retain their separate 25 MiB allowance.
- Each public asset or cache file is at most 10 MiB; the combined asset inventory has at most 1,000 files.
- The complete deployment archive is at most 30 MiB compressed and 64 MiB expanded, including cache, public files, Worker modules, migrations and archive metadata.
Keep the app portable
Use ordinary Next.js routes and configuration. The hosting service supplies its build adapter; do not create a customer Wrangler project merely to make deployment work. Private auth credentials and database access belong in the server runtime, notNEXT_PUBLIC_ values.
Keep existing authentication unless you choose to replace it. Configure exact callback and logout URLs for the environment you are testing. Application authentication · Runtime secrets.
Hosted server code reads private bindings through getCloudflareContext({ async: true }).env. A localhost-only runtime factory is a test adapter, not a production backend. Preserve ordinary route handlers and use the application database client and private file client from that context.
For a library that uses WebAssembly, prefer a runtime-specific package export: a workerd entry can statically import the pinned .wasm modules, while a separate Node entry loads the same bytes locally. Preserve the library’s license and real validation. The shared build pipeline carries validated Workers-compatible JavaScript, MJS and WASM modules for Next.js, TanStack Start edge, Vite edge companions and plain functions Workers. Auxiliary modules are at most 5 MiB each; the Worker and artifact budgets still apply. The immutable artifact keeps those modules together; a successful Node build alone does not prove the Worker module can start.
Scheduled work is declared as functions.crons and runs scheduled from the default export of src/ohmyhost/worker.ts, a module the platform bundles outside the Next.js build; see Functions and cron.
Next.js SSR receives a fresh nonce on every rendered script/style tag; style attributes, event handlers and later client-created unnonced tags remain blocked. External connections/resources/frames/workers need explicit browser declarations; media/microphone needs the server HTML opt-in.
Images and themes under CSP
A nonce permits a script or style tag; it does not permit an inlinestyle attribute. If an already unoptimized next/image component emits blocked inline styles, use a native image with the same URL, dimensions, classes and alternative text. Keep its loading behavior and put styling in the stylesheet:
loading="eager" and fetchPriority="high" instead of lazy loading.
Libraries that insert styles after hydration must use the trusted response nonce from meta[property="csp-nonce"]. For next-themes 0.4.6, read it before the provider’s first client effect:
unsafe-inline allowance.
Files read by server code
The platform includes non-executable files recorded in Next.js output-file traces (.nft.json) as read-only data modules in the Worker. Use ordinary Node filesystem reads for those deployed files and keep them inside the application root. When Next cannot infer a dynamic filename, declare the required files with its normal outputFileTracingIncludes configuration. Missing or invalid traced files fail packaging, and data modules count toward the Worker size limits.
These runtime files receive no public URL from this mechanism. Files intentionally placed in public/ still follow Next.js public-asset behavior. Do not bundle secrets as content; use runtime secrets. Runtime filesystem writes do not persist application data: use files or the database.
Compile MDX during the build
Workers does not support runtime code generation witheval or new Function. An MDX library that evaluates compiled strings at request time therefore needs a build-time integration, even if the same code works under local Node.js.
Compile MDX with @mdx-js/mdx using outputFormat: "program", write ordinary ES modules and import them through a static component registry. Invoke the deterministic generator from the Next configuration so the build script stays next build. Keep the source JSON or MDX as the canonical content and preserve the application’s component mapping, GFM plugins, links and embedded markup. Routes render the generated components without a runtime evaluator. The generator must also work in a fresh checkout.