Skip to main content

Nuxt 3 502 Bad Gateway — fix guide

TL;DR

How to diagnose and fix 502 Bad Gateway errors when running self-hosted Nuxt 3 behind nginx.

Key facts

Topic
Production error triage
Stack
Nuxt.js / nginx / Linux

TL;DR

A 502 Bad Gateway from nginx in front of Nuxt 3 means nginx attempted to proxy a request to the Nitro server but received no valid response. The Nitro process has either crashed, is not listening on the port nginx expects, or is taking too long to render an SSR page. This is the most common Nuxt 3 self-hosting issue.

Common causes

  • Nitro process crashed — PM2 shows the process in errored or stopped state
  • Port mismatch — Nitro defaults to port 3000, but NUXT_PORT overrides this; nginx must proxy to the correct port
  • Bind address mismatch — Nitro listening on 127.0.0.1 while nginx proxies to 0.0.0.0, or vice versa
  • Upstream timeout — complex SSR pages taking longer than nginx's proxy_read_timeout (default 60s)
  • Process not started after deploynuxt build completed but nobody started the production server

Diagnosis workflow

Check if the Nitro server is running and listening:

pm2 status
ss -tlnp | grep :3000

If the port shows nothing, the Nitro process is not running. Check PM2 logs:

pm2 logs nuxt-app --lines 50

Review nginx error logs for the upstream failure:

tail -100 /var/log/nginx/error.log

Look for connect() failed (111: Connection refused) (process not running) or upstream timed out (SSR too slow).

Test the Nitro server directly, bypassing nginx:

curl -I http://127.0.0.1:3000/

Fix the port configuration

Nuxt 3 uses environment variables to configure the Nitro server's listen address:

NUXT_HOST=127.0.0.1
NUXT_PORT=3000

Set these in your PM2 ecosystem file or systemd service. Then ensure nginx proxies to the same address:

upstream nuxt_app {
    server 127.0.0.1:3000;
    keepalive 64;
}

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://nuxt_app;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_read_timeout 60s;
        proxy_connect_timeout 10s;
    }

    location /_nuxt/ {
        alias /var/www/nuxt-app/.output/public/_nuxt/;
        expires 365d;
        add_header Cache-Control "public, immutable";
        access_log off;
    }
}

Test and reload:

sudo nginx -t && sudo systemctl reload nginx

Fixing timeout-related 502s

If SSR pages are legitimately slow (data-heavy pages during cold start), increase nginx's upstream timeout:

proxy_read_timeout 120s;
proxy_connect_timeout 15s;
proxy_send_timeout 60s;

However, slow SSR is a symptom — consider adding server-side caching with Nitro's built-in route rules:

// nuxt.config.ts
export default defineNuxtConfig({
  routeRules: {
    '/blog/**': { swr: 3600 },
    '/api/**': { cors: true, swr: false },
  },
})

Starting Nitro after a deploy

A complete deploy sequence for Nuxt 3:

cd /var/www/nuxt-app
git pull origin main
npm ci
npx nuxt build

pm2 restart nuxt-app
sleep 3
curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:3000/

If the health check fails, check .output/server/index.mjs exists and review PM2 error logs.

Where Reflex helps

Reflex correlates nginx 502 errors with Nitro process health in real time. When it detects a 502 spike, Reflex checks whether the Nuxt process is listening, restarts it via PM2 if needed, verifies the 502 rate drops, and alerts your team with a full incident timeline including the root cause. See How it works.