Nginx 502 Bad Gateway, and the Four Other Errors Nginx Throws Most

Nginx returns 502 Bad Gateway when it acts as a proxy and the app behind it gives no valid answer: the app is down, listening on another port or socket, or crashed mid-reply. Start the app, make proxy_pass point at the address it really listens on, then read the nginx error log for the exact reason.

Which nginx error points where
StatusWhat nginx is telling youFirst place to look
502 Bad GatewayThe upstream app sent nothing usableIs the app running on the proxy_pass address?
504 Gateway TimeoutThe upstream app was too slowproxy_read_timeout (default 60s)
403 ForbiddenNginx refuses to serve the pathMissing index file, file permissions, deny rules
404 Not FoundNo file at the path nginx builtroot versus alias, try_files
413 Request Entity Too LargeThe upload body is over the limitclient_max_body_size (default 1m)

Updated

Pick the status code you see, or type a word from your error log, and the finder narrows the list to the matching causes. Each card names the cause, the check that confirms it, and a config snippet you can adapt. Every card is shown until you filter.

Nginx error finder

Status code

Words from your error log

11 matching causes

502: The app behind nginx is not running, so the connection is refused.

Check: Curl the proxy_pass address from the nginx machine. A refused connection confirms it.

Fix: Start the app under a process manager that restarts it after a crash.

# confirm from the nginx host
curl -i http://127.0.0.1:3000/

502: proxy_pass points at a port or socket the app does not listen on.

Check: Compare the address in proxy_pass with the one the app prints when it starts.

Fix: Make the two match exactly, including the host part.

location / {
    proxy_pass http://127.0.0.1:3000;
}

502: The app sends response headers larger than the buffer nginx reads them into, for example many cookies.

Check: Look for a log line that mentions the header being too big.

Fix: Raise proxy_buffer_size for that location. Its default is one memory page, 4k or 8k.

location / {
    proxy_pass http://127.0.0.1:3000;
    proxy_buffer_size 16k;
    proxy_buffers 8 16k;
}

504: The app took longer than proxy_read_timeout between two reads, 60 seconds by default.

Check: Time the same request against the app directly with curl.

Fix: Speed up the endpoint or move the work to a background job. Raise the timeout only for that location.

location /reports/ {
    proxy_pass http://127.0.0.1:3000;
    proxy_read_timeout 180s;
}

504: Nginx could not even open a connection within proxy_connect_timeout, 60 seconds by default.

Check: Check for a firewall or a host that is unreachable from the proxy.

Fix: Fix the network path. The nginx docs note this timeout cannot usually exceed 75 seconds, so raising it does not help.

proxy_connect_timeout 10s;

403: A URL ending in a slash has no index file and autoindex is off.

Check: List the folder that root points at and look for index.html.

Fix: Add the index file, or send the request to your app with try_files.

location / {
    root /var/www/site;
    index index.html;
    try_files $uri $uri/ /index.html;
}

403: The nginx worker user cannot read the file or enter one of the folders above it.

Check: Look for a permission denied line in the error log naming the path.

Fix: Give the worker user read access to the files and execute access to every parent folder.

namei -l /var/www/site/index.html

403: An allow or deny rule matched the visitor. Rules are checked in order and the first match wins.

Check: Read every allow and deny line that applies to the location, top to bottom.

Fix: Reorder the rules so the specific allow comes before the broad deny all.

location /admin/ {
    allow 203.0.113.0/24;
    deny  all;
}

404: root was used where alias was meant, so nginx adds the location prefix to the path twice.

Check: Work out the file path: root appends the whole URI, alias replaces the matched prefix.

Fix: Use alias, with a trailing slash on both sides, when the folder name differs from the URL.

location /images/ {
    alias /data/uploads/;
}

404: A single page app route such as /dashboard has no file on disk.

Check: The home page loads but a refresh on any inner route fails.

Fix: Fall back to index.html with try_files so the app's router handles the path.

location / {
    try_files $uri $uri/ /index.html;
}

413: The request body is larger than client_max_body_size, 1m by default.

Check: Uploads under a megabyte work and larger ones fail.

Fix: Raise the limit for the upload location only.

location /upload {
    client_max_body_size 25m;
    proxy_pass http://127.0.0.1:3000;
}

A proxy block that avoids the usual mistakes

A starting point for an app on port 3000. It passes the original host and scheme through, sets explicit timeouts and leaves room for larger headers.

server block for a Node or Python app

Test with nginx -t, then reload. Adjust the port and the limits to your app.

server {
    listen 80;
    server_name example.com;

    client_max_body_size 10m;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_connect_timeout 10s;
        proxy_read_timeout 60s;
        proxy_buffer_size 16k;
        proxy_buffers 8 16k;
    }
}

Why a proxy produces errors it did not cause

In most setups nginx sits in front of another program: a Node server, PHP-FPM, a Python app. Nginx forwards the request and relays the reply. When that program is missing, slow or broken, nginx is the one that has to answer the browser, so the error carries nginx's name even though the fault sits one hop back.

That is why 502 and 504 are gateway codes. They describe what happened between the proxy and the app, not between the browser and the proxy.

Read the error log before changing anything

The browser only shows a status code. The nginx error log records why: a refused connection, a timeout, a missing file, a permissions failure. Matching on that reason saves you from raising timeouts when the app simply is not running.

How it works

  1. Note the status code in the browser and copy the matching line from the nginx error log.
  2. Choose that code in the finder, or type a distinctive word from the log line into the search box.
  3. Run the check on each remaining card until one confirms the cause.
  4. Apply the fix, test the config with nginx -t, then reload nginx.

Tips

Frequently asked questions

Is a 502 error my fault or the server's?

As a visitor, nothing you do causes it. The proxy reached for the application behind it and got no valid reply. As the site owner it is your app or your proxy settings, so start by checking that the upstream process is running.

What is the difference between 502 and 504 in nginx?

A 502 means the upstream answered badly or refused the connection. A 504 means nginx waited and nothing arrived in time. The first points at a stopped or crashing app, the second at slow work or a timeout set too low.

How do I raise the nginx upload size limit?

Set client_max_body_size in the http, server or location block, for example client_max_body_size 20m;. The default is 1m. A value of 0 turns the size check off, which is rarely what you want on a public site.

Why does nginx return 403 for a folder?

When the URL ends in a slash, nginx looks for an index file such as index.html. If none exists and autoindex is off, which is the default, there is nothing it may serve. Add the index file or route the request with try_files.

Where is the nginx error log?

Its location is set by the error_log directive in your config. Search your nginx.conf and included files for error_log to find it, since packages and containers place it differently.

Related: HTTP status codes reference, Fix too many redirects, HTTP 405 Method Not Allowed, 401 vs 403, Redirect checker.

Start for free or see all free tools. We.Inc is free to start, with no card needed. AI building is on paid plans from $20 a month.

More in Guides

We.Inc is an AI-powered website builder you can resell under your own brand. Launch a branded client dashboard, bill on Stripe Connect, and deliver AI-generated websites in minutes. White-label plans start at $99 a month for 25 client sites, with a 7-day free trial and no per-site fees.

Product

Who It's For

Features

Resources

Company

View Sitemap