Skip to content

Before You Deploy

These steps apply to every deployment, regardless of platform.

  • Node.js 22 or newer. The project checks for this at install time and will refuse to install on older versions.
  • A working production build. Run the build on your machine first. If it fails locally, it will fail on the server:
    Terminal window
    cd web
    npm ci
    npm run build
  • Production values for every environment variable. Every project has a web/.env.example file. Each variable in that file needs a production value before you deploy. Variables starting with NEXT_PUBLIC_ are baked into the browser bundle during the build, so they must be set before you run npm run build, not afterwards.
  • Network access from your server to any external services. If your project talks to a backend API, the server you deploy to must be able to reach those URLs. If the API is on the same machine, that’s localhost. If it’s hosted elsewhere, your firewall needs to allow outbound HTTPS to it.

You need a reverse proxy in front of your app. A reverse proxy is a web server that sits between the browser and your app: the browser connects to the proxy on port 443 (HTTPS), and the proxy forwards the request to your app on port 3000, which isn’t exposed to the internet.

The Next.js process handles your application logic. The proxy handles TLS, compression, and logging, and maps a hostname to a port so users don’t have to type :3000 in the URL.

The proxy needs to pass three headers to your app so the app knows what the original request looked like:

HeaderWhat it tells your app
HostThe hostname the user typed (e.g. app.example.com).
X-Forwarded-ProtoWhether the original request was http or https. The app uses this to build correct absolute URLs and decide whether to mark cookies as Secure.
X-Forwarded-ForThe real client IP address.

The Windows, Linux, and Docker guides each show how to configure these headers for their respective proxy.

If your project uses cookie-based authentication, the frontend and the auth endpoint must sit on the same registrable domain. The registrable domain is the part you buy from a registrar. For app.example.com, that’s example.com. For auth.example.com, also example.com.

Modern browsers only send auth cookies on requests that share this domain. Ports don’t factor into the comparison.

FrontendAuth endpointWorks?
app.acme.comauth.acme.comYes — both acme.com
app.acme.comacme-auth.comNo — different domains
localhost:3000localhost:10010Yes — ports don’t affect same-site

If you can’t put both services under one domain, use the reverse proxy to expose the auth endpoint under a path on your main domain (e.g. https://app.example.com/auth/... proxying through to the real auth service). The browser only ever sees one domain.

If your project doesn’t use authentication, skip this section.

Before building, edit web/next.config.ts and add output: 'standalone' to the nextConfig object:

import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
output: 'standalone',
};
export default nextConfig;

This tells Next.js to produce a self-contained folder (web/.next/standalone/) with only the files needed to run the app. The result is much smaller and easier to ship to a server than copying the full project with all of node_modules.

After running the build, you’ll have three folders to copy to your server:

FolderWhat it contains
web/.next/standalone/The app and its minimal dependencies. Contains a server.js entry point.
web/.next/static/Compiled CSS, JS, and fonts. Goes inside <deploy-folder>/.next/static/.
web/public/Static assets (favicon, images, etc.).

Now pick the guide for your target environment: