Fix guides / Replit: published app fails, health check or port errors

Your Replit app runs in Preview but publishing fails: fixing port and health check errors

The app runs fine in the Replit workspace Preview. You press Publish and it fails at the last step, keeps restarting, or goes live and immediately errors. Most of these failures come from five settings, none of which the Agent can see from inside Preview.

Symptoms

  • Publishing fails at the final promote or health check step.
  • Logs show the server started, but on localhost or 127.0.0.1.
  • Errors about a port you never configured, or about HTTP and HTTPS.
  • The published app loads but API calls, login, or payments fail with undefined values.

Step 1: listen on 0.0.0.0 and the expected port

Published apps must listen on 0.0.0.0. Listening only on localhost works in some previews but not in production. Read the port from the environment when it is provided:

// Node / Express
const port = Number(process.env.PORT) || 5000;
app.listen(port, '0.0.0.0', () => console.log(`listening on ${port}`));

# Python / Flask with gunicorn (start command)
gunicorn --bind=0.0.0.0:5000 main:app

// Vite preview of a built front end
vite preview --host 0.0.0.0 --port 5000

Step 2: clean up the ports config

If your .replit file has a [[ports]] section, automatic port detection is turned off. Autoscale and Reserved VM deployments expose exactly one external port, and it must be 80, mapped to the port your server listens on:

[[ports]]
localPort = 5000
externalPort = 80

Remove extra [[ports]] entries left over from dev servers, database tools, or old experiments. More than one exposed port, or the exposed one bound to localhost, makes publishing fail.

Step 3: add production secrets

Secrets set in the workspace do not automatically carry over to the published app. Open the Publishing pane and add every secret the app needs (database URL, Supabase keys, Stripe keys, API keys). Missing secrets cause errors about undefined values, failed API calls, login errors, and payment failures that never showed up in Preview.

Step 4: answer the health check within 5 seconds

Before going live, Replit sends a request to your home page. If it takes more than about five seconds, publishing can fail at the final step. Do not run migrations, seed data, or warm caches before calling listen. Start the server first and do slow work afterwards or on demand. A tiny route helps:

app.get('/health', (req, res) => res.send('ok'));

Step 5: do not redirect the health check to HTTPS

Many AI-written apps force HTTPS with middleware that reads X-Forwarded-Proto and defaults to http when the header is missing. Replit's internal health check calls your server directly without that header, gets redirected to HTTPS, and fails. Only redirect when the header is present and says http:

// Express
app.use((req, res, next) => {
  if (req.headers['x-forwarded-proto'] === 'http') {
    return res.redirect(301, `https://${req.headers.host}${req.url}`);
  }
  next();
});

Step 6: make sure the build and run commands are right

In the Publishing pane, check that the build command creates the production files (for example npm run build) and the run command starts a long-running server, not a one-off script. A process that exits is restarted again and again. Then open the Logs tab of the deployment and read the first error, not the last one.

Still stuck? No shame in it: these bugs hide in settings your AI builder cannot see. Send us the error and your repo and we will fix it as a pull request within 24 hours, explained in plain English, for $49. Not fixed means full refund.
Request a fix

Last updated 2026-10-19.