How to Fix: output standalone breaks serving static from public and everything in _next/*
Enabling Next.js standalone output often makes the app boot successfully but silently breaks files from /public and requests under /_next/* when you deploy only the generated standalone server. The reason is simple: standalone does not bundle every static asset into the server folder. It traces runtime server dependencies, but your browser assets still need to be copied and served from the correct paths.
Table of Contents
Understanding the Root Cause
When you set output: “standalone” in next.config.js or next.config.ts, Next.js creates a minimal runtime in .next/standalone. That folder is designed to contain the server code and only the files required to execute it in production.
The key detail is that standalone output is not a complete deployment artifact by itself. Static browser assets are still expected to exist outside the traced server bundle:
- Public assets remain in the public directory.
- Compiled client assets such as JavaScript chunks, CSS, build manifests, and image metadata remain in .next/static.
If you copy only .next/standalone into a container or server, the Node process can start, but incoming requests for:
- /favicon.ico
- /robots.txt
- /images/*
- /_next/static/*
- other browser-loaded assets
will fail because those files were never included in the deployed filesystem.
This is why the issue looks confusing: the application server works, but the browser sees broken CSS, missing JavaScript, 404s for public files, or blank pages because the client bundles under /_next/* cannot be fetched.
Technically, Next.js expects this production layout:
- .next/standalone for the server runtime
- .next/static for generated static assets
- public for user-managed static files
If any one of these is missing in the final runtime image, asset serving breaks.
Step-by-Step Solution
The fix is to deploy the standalone server together with the required static directories.
1. Enable standalone output
In your Next.js config:
import type { NextConfig } from 'next'

const nextConfig: NextConfig = {
 output: 'standalone',
}

export default nextConfig
Build the app:
npm run build
After the build, verify these paths exist:
.next/standalone
.next/static
public
2. Copy the missing asset directories into the runtime artifact
If you are packaging manually, copy all required directories:
cp -r .next/standalone ./deploy
cp -r .next/static ./deploy/.next/static
cp -r public ./deploy/public
Your deployment directory should end up looking like this:
deploy/
 .next/
 static/
 public/
 server.js
Depending on your Next.js version, server.js is usually generated inside .next/standalone as the entrypoint.
3. Fix Docker deployments
This bug commonly appears in Docker because many images copy only the standalone folder. The correct pattern is to copy .next/standalone, .next/static, and public.
Use a production Dockerfile like this:
FROM node:20-alpine AS base
WORKDIR /app

FROM base AS deps
COPY package.json package-lock.json ./
RUN npm ci

FROM base AS builder
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build

FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production

COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public

EXPOSE 3000
CMD ["node", "server.js"]
This is the most important part of the fix:
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public
Without those lines, requests for /_next/static/* and files in /public will fail.
4. Verify locally before redeploying
Run the standalone output exactly as production will run it:
node .next/standalone/server.js
Then test:
If the page HTML loads but CSS or JavaScript does not, your asset directories are still missing or copied to the wrong location.
5. Keep the folder structure exact
Next.js resolves asset URLs using fixed conventions. That means:
- .next/static must stay under .next/static
- public must stay at the app root as ./public
- server.js must run relative to that structure
Do not flatten or rename those directories in your deploy artifact unless you also place a reverse proxy in front that remaps every path correctly.
6. If using a custom server or reverse proxy, preserve Next paths
If Nginx, Traefik, or another proxy sits in front of the app, ensure it forwards these paths untouched:
- /_next/static/*
- /public-derived files such as /favicon.ico
An example Nginx snippet:
location /_next/static/ {
 proxy_pass http://nextjs:3000;
}

location /favicon.ico {
 proxy_pass http://nextjs:3000;
}

location / {
 proxy_pass http://nextjs:3000;
}
If your proxy strips prefixes, rewrites incorrectly, or blocks underscore-prefixed paths, the app may appear partially broken even when the build output is correct.
Common Edge Cases
1. basePath or assetPrefix is configured
If your app uses basePath or assetPrefix, the generated asset URLs will change. For example, assets may be requested from /docs/_next/static/* instead of /_next/static/*. In that case, your proxy and hosting setup must support the prefixed path.
const nextConfig = {
 output: 'standalone',
 basePath: '/docs',
}
If the app is deployed at the domain root while basePath is enabled, all static requests will look broken.
2. CDN or proxy caching stale manifests
After a new deployment, old HTML may reference outdated chunk filenames. If your CDN caches documents but not new static files consistently, the browser may request non-existent files under /_next/static/chunks/*.
Invalidate the CDN or ensure HTML and static assets roll out atomically.
3. public directory not present at build or runtime
If your CI pipeline excludes the public folder using a bad .dockerignore or artifact rule, public assets will disappear in production even though they work locally.
Check that files like favicon.ico, robots.txt, and custom images are not being filtered out.
4. Running from the wrong working directory
The standalone server expects a predictable filesystem layout. If your process manager launches server.js from an unexpected directory or you copy files into a nested path incorrectly, asset lookup can fail.
Always test the exact runtime structure inside the container or server shell.
5. Custom rewrites masking 404s
Some apps rewrite unknown paths to a catch-all route. That can hide the real issue by returning HTML instead of a proper 404 for missing static files. In the browser, this often appears as MIME type errors or scripts failing to execute.
Inspect the network tab and confirm asset requests are returning the expected content-type and status code.
6. Mixing static export expectations with standalone server output
output: ‘standalone’ is for running a production Node server. It is not the same as a fully static export workflow. If your deployment target expects plain static files only, you may need a different strategy such as export-compatible routing rather than standalone server output.
FAQ
Why does the app render HTML but CSS and JavaScript are missing?
Because the Node server from .next/standalone is running, but the browser assets from .next/static were not deployed. The initial HTML response works, then client asset requests fail.
Do I need to copy the entire .next directory?
No. For a typical standalone deployment, you usually need .next/standalone plus .next/static and public. Copying the whole .next folder is unnecessary unless your deployment process specifically depends on it.
Why are files from /public failing if standalone is enabled?
Because public is not bundled into the standalone server output. It remains a separate runtime directory and must be copied alongside the server artifact.
The reliable fix for this GitHub issue is to treat .next/standalone as only one part of the deployment package. Ship it together with .next/static and public, preserve the directory structure, and verify your proxy does not rewrite /_next/* paths incorrectly. Once those pieces are present, standalone mode works as expected.