Common Errors #
Most Nginx problems in production environments fall into the same error categories repeatedly. The ability to recognize these error patterns makes problem diagnosis much faster. Instead of starting the investigation process from zero every time an obstacle occurs, we can directly head to the most common root causes based on the recorded log messages.
In this article, we’ll discuss in depth the various HTTP errors most frequently encountered in Nginx, how to read their error log messages, systematic investigation steps for each case, and practical solutions to overcome them.
Nginx Response Status Decision Diagram #
Before dissecting each error one by one, we need to understand how Nginx decides to send a certain response status code to the client browser. Here’s the response status determination decision flow in Nginx:
flowchart TD
Req["Request Arrives at Nginx"] --> Match{"Does the Route Match?"}
Match -->|"No"| Err404["HTTP 404 Not Found<br/>(Check root/alias & try_files)"]
Match -->|"Yes"| Auth{"Needs Authorization / IP Whitelist?"}
Auth -->|"Denied / Insufficient File Permissions"| Err403["HTTP 403 Forbidden<br/>(Check chown/chmod & allow/deny)"]
Auth -->|"Passed"| Size{"Is the Request Body > limit?"}
Size -->|"Yes"| Err413["HTTP 413 Request Entity Too Large<br/>(Check client_max_body_size)"]
Size -->|"No"| Conn{"Connection to the Backend Upstream"}
Conn -->|"Backend Dead / Wrong Port"| Err502["HTTP 502 Bad Gateway<br/>(Connection Refused / Closed)"]
Conn -->|"Backend Slow to respond"| Err504["HTTP 504 Gateway Timeout<br/>(proxy_read_timeout exceeded)"]
Conn -->|"Response Successful"| OK["HTTP 200 OK"]
classDef default fill:#f9f9f9,stroke:#d1d5db,stroke-width:1px,color:#111827;
classDef errStyle fill:#fef2f2,stroke:#ef4444,stroke-width:2px,color:#991b1b;
classDef okStyle fill:#f0fdf4,stroke:#15803d,stroke-width:2px,color:#166534;
class Err404,Err403,Err413,Err502,Err504 errStyle;
class OK okStyle;HTTP 502 Bad Gateway #
The 502 Bad Gateway error indicates that Nginx, acting as a proxy, successfully received the request from the client, but when trying to forward it to the backend application server (like Node.js, PHP-FPM, or Python Gunicorn), Nginx received an invalid response or no response at all (connection cut/refused).
1. Anatomy of the Error Log Message #
When a 502 error occurs, we must immediately check the Nginx error log file. Here are the error log message variations that often appear along with their interpretations:
connect() failed (111: Connection refused) while connecting to upstream- Meaning: Nginx tried to contact the backend IP address and port, but the operating system refused the connection because no process is listening on the target port.
connect() failed (2: No such file or directory) while connecting to upstream- Meaning: This happens when we use a Unix Domain Socket (e.g., a
.sockfile), but the physical socket file doesn’t exist on the server disk.
- Meaning: This happens when we use a Unix Domain Socket (e.g., a
recv() failed (104: Connection reset by peer) while reading response header from upstream- Meaning: Nginx successfully connected to the backend, but the backend suddenly cut the TCP connection while Nginx was waiting for the response header data to be sent. This usually happens because the backend crashed (sudden death) due to running out of memory (out of memory) or a fatal error in the application code.
2. Systematic Investigation Steps #
If we find a 502 error on our server, do the following steps:
- Step 1: Check whether the backend application process is running.
# For PHP-FPM sudo systemctl status php8.2-fpm # For Node.js managed by PM2 pm2 status # For Python Gunicorn/Uvicorn managed by systemd sudo systemctl status gunicorn - Step 2: If the process is running, check the port or socket used by that backend. Make sure that port is the same as what we wrote in the Nginx
proxy_passorfastcgi_passdirective.# Check the TCP ports currently listening sudo ss -tlnp | grep -E "3000|8000|9000" # Check whether the Unix socket file exists ls -la /run/php/php8.2-fpm.sock - Step 3: Do a direct connection test to the backend through the server localhost using the
curlutility to isolate Nginx’s role:If the command above returns a successful response, the problem is on the Nginx-to-backend communication path (e.g., socket access rights or IP configuration issues). Ifcurl -I http://127.0.0.1:3000/curlfails directly, then our backend application is the one with the problem.
3. Practical Solutions #
- Use failover configuration: If we use a backend cluster, make sure Nginx doesn’t keep sending requests to the dead node using the
max_failsandfail_timeoutparameters in the upstream block. - Fix Unix socket access rights: If using a socket, make sure the Nginx system user (
www-data) has read and write permissions to that socket file.# Example of fixing PHP-FPM socket ownership sudo chown www-data:www-data /run/php/php8.2-fpm.sock sudo chmod 660 /run/php/php8.2-fpm.sock
HTTP 504 Gateway Timeout #
The 504 Gateway Timeout error means Nginx successfully connected to the upstream backend server, but the backend took too long to process the client request and didn’t send any response within the timeout limit set by the Nginx configuration.
1. Anatomy of the Error Log Message #
The error log message recorded for a 504 case usually reads:
2026/06/16 14:15:00 [error] 12345#12345: *998 upstream timed out (110: Connection timed out) while reading response header from upstream, client: 203.0.113.50, server: app.unisbadri.com, request: "GET /api/export-data HTTP/1.1", upstream: "http://127.0.0.1:3000/api/export-data", host: "app.unisbadri.com"
2. Systematic Investigation Steps #
- Step 1: Check the endpoint URL triggering the 504 error. Does that request involve very heavy database queries, large file processing (like PDF/Excel exports), or slow third-party external API calls?
- Step 2: Check the server resource utility when the request is sent. Does the server CPU spike to 100%? Is RAM exhausted, triggering slow memory swapping?
# Monitor resource usage in real-time htop - Step 3: Check our backend application logs to see how long the application spent processing the request before the connection was closed by Nginx.
3. Practical Solutions #
- Raise the Nginx timeout duration: If certain routes genuinely need long processing times (like report generators), we must raise the timeout specifically for that location block:
location /api/export-data { proxy_pass http://nodejs_backend; # Raise the response waiting time limit to 5 minutes (300 seconds) proxy_read_timeout 300s; proxy_send_timeout 300s; proxy_connect_timeout 300s; } - FastCGI Timeout Tuning (for PHP-FPM):Note: Make sure we also raise the
location ~ \.php$ { include fastcgi_params; fastcgi_pass unix:/run/php/php8.2-fpm.sock; # Raise the PHP FastCGI timeout fastcgi_read_timeout 300s; }max_execution_timeconfiguration in the PHP-FPMphp.inifile to align with the Nginx timeout.
HTTP 403 Forbidden #
The 403 Forbidden error shows that the server understands what the client wants to access, but the server consciously refuses to give that access due to file permission problems, directory index prohibitions, or security rules blocking the request.
1. Anatomy of the Error Log Message #
The log message for 403 is very specific about the cause:
directory index of "/var/www/html/" is forbidden- Meaning: The client accessed a directory URL (like
/or/images/), but there’s no default index file (likeindex.htmlorindex.php) inside that folder, while theautoindexdirective (which displays file lists) is disabled.
- Meaning: The client accessed a directory URL (like
open() "/var/www/html/secret.txt" failed (13: Permission denied)- Meaning: The physical file exists on disk, but the system user running the Nginx worker process (usually
www-dataornginx) doesn’t have read access rights to that file or its parent folder.
- Meaning: The physical file exists on disk, but the system user running the Nginx worker process (usually
2. Systematic Investigation Steps #
- Step 1: Check whether the default index file (e.g.,
index.html) actually exists in our application’s root directory. - Step 2: Check the ownership and permissions of the target file and folder.
ls -la /var/www/html/ - Step 3: Check whether there are active security rules like the
allow/denymodule in our Nginx configuration that accidentally block our client’s IP. - Step 4: If using a RedHat-based distro (CentOS/RHEL/Fedora), check the SELinux security status. SELinux often blocks Nginx access to folders outside the standard
/usr/share/nginx/html.
3. Practical Solutions #
- Fix file access rights recursively: Make sure directories have
755permissions (accessible and passable) and files have644permissions (readable by clients, only writable by the owner):# Change ownership to the Nginx user sudo chown -R www-data:www-data /var/www/html/ # Set folder permissions to 755 sudo find /var/www/html/ -type d -exec chmod 755 {} \; # Set file permissions to 644 sudo find /var/www/html/ -type f -exec chmod 644 {} \; - Configure the SELinux context:
# Allow Nginx to read files in custom folders sudo chcon -Rt httpd_sys_content_t /var/www/html/ - Enable autoindex (only for download/public folders):
location /downloads/ { autoindex on; # Display the file list in the client browser }
HTTP 404 Not Found #
The 404 Not Found error means Nginx couldn’t find the physical file or resource requested by the client browser in the configured root directory.
1. Anatomy of the Error Log Message #
The log message for 404 usually reads:
2026/06/16 14:20:00 [error] 12345#12345: *1002 open() "/var/www/html/dashboard" failed (2: No such file or directory), client: 203.0.113.50, server: app.unisbadri.com, request: "GET /dashboard HTTP/1.1", host: "app.unisbadri.com"
2. Systematic Investigation Steps #
- Step 1: Check the Nginx error log file to see the physical path location where Nginx tried to find the file. In the log example above, Nginx searched for a file named
dashboardinside the/var/www/html/folder. - Step 2: Check the
rootoraliasdirective in the virtual host configuration file. A one-character typo in the root path will make Nginx look in the wrong folder. - Step 3: Check whether that route is a dynamic route belonging to a Single Page Application (React/Vue). If so, Nginx physically doesn’t have a
/dashboardfile on disk because routing is handled in the client browser.
3. Practical Solutions #
- Implement fallback routing for SPAs: Make sure we include the
try_filesdirective so requests are redirected back to the mainindex.htmlfile:location / { root /var/www/my-spa-app/dist; try_files $uri $uri/ /index.html; } - Check the Trailing Slash on Proxy Pass: Remember the trailing slash rule. If we write:Requests to
location /api { proxy_pass http://backend/; # Trailing slash at the end }/api/userswill be forwarded to the backend as/users(the/apicharacters are discarded). This often triggers 404 errors on our backend application server side. Make sure the location and proxy target writing are in sync.
HTTP 413 Request Entity Too Large #
The 413 Request Entity Too Large error happens when the client browser tries to send request body data (like image or video file uploads) whose size exceeds the maximum limit allowed by the Nginx configuration.
1. Anatomy of the Error Log Message #
The log message for 413 reads:
2026/06/16 14:22:00 [warn] 12345#12345: *1015 client intended to send too large body: 15485724 bytes, client: 203.0.113.50, server: app.unisbadri.com, request: "POST /api/upload HTTP/1.1", host: "app.unisbadri.com"
2. Practical Solutions #
By default, Nginx limits the client request body size to 1 Megabyte. To allow large file uploads, we must raise this limit using the client_max_body_size directive.
We can apply it at the global http level, the server block level, or specifically in the upload location block:
server {
listen 80;
server_name app.unisbadri.com;
# Global default limit for this server block (10MB)
client_max_body_size 10m;
location /api/upload-video {
# Custom limit specifically for video uploads (500MB)
client_max_body_size 500m;
proxy_pass http://upload_backend;
}
}
Common SSL/TLS Problems #
SSL/TLS configuration problems in Nginx can make our website completely inaccessible and show scary security warnings to visitors.
1. Browser Error: ERR_SSL_PROTOCOL_ERROR or SSL_ERROR_RX_RECORD_TOO_LONG
#
- Symptoms: The browser fails to do a secure SSL handshake.
- Most common cause: We configured Nginx to listen on the HTTPS port (443), but forgot to turn on the
ssldirective on that listen line, so Nginx sends plain HTTP responses over the HTTPS port. - Solution: Make sure the
sslparameter is written explicitly in thelistendirective:# Wrong configuration: listen 443; # Correct configuration: listen 443 ssl http2;
2. Browser Error: ERR_CERT_AUTHORITY_INVALID (Untrusted Certificate)
#
- Symptoms: The browser shows a “Your connection is not private (insecure)” warning and refuses to load the web page.
- Most common cause: We use a Let’s Encrypt or other public CA certificate, but in the Nginx
ssl_certificatedirective, we only point the file to our domain’s main certificate (cert.pem) without including the intermediate CA certificate. This breaks the Chain of Trust in the client browser. - Solution: Always use the
fullchain.pemfile (which combines our domain certificate and the intermediate CA) in our Nginx settings:# Wrong: ssl_certificate /etc/letsencrypt/live/app.unisbadri.com/cert.pem; # Correct: ssl_certificate /etc/letsencrypt/live/app.unisbadri.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/app.unisbadri.com/privkey.pem;
3. Browser Error: ERR_CERT_COMMON_NAME_INVALID (Name Doesn’t Match)
#
- Symptoms: The browser warns that the security certificate isn’t for our site’s domain.
- Most common cause: The
server_namevalue accessed by the client isn’t listed in the Subject Alternative Name (SAN) field of our active SSL certificate. - Solution: Make sure we request an SSL certificate covering the main domain along with wildcard sub-domains (if needed) using Certbot, and point the correct certificate file to the corresponding server block.
4. How to Check Certificate Details via CLI #
We can diagnose SSL problems directly from the terminal without opening a browser using the following OpenSSL utility command:
# Display the subject details, certificate issuer, and expiration date
echo | openssl s_client -connect app.unisbadri.com:443 -servername app.unisbadri.com 2>/dev/null | \
openssl x509 -noout -text | grep -E "Subject:|Issuer:|Not After:|DNS:"
Nginx Won’t Start / Reload #
Sometimes Nginx refuses to restart or reload its configuration because of network conflicts or system file issues.
1. Port 80 or 443 Already in Use (Address already in use)
#
- Symptoms: Nginx fails to start and the system log shows the error message:
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use) - Cause: Another web server software (like Apache
apache2or Caddy) is currently running on the same port. - Diagnosis & Solution Steps:
- Find which process is currently occupying port 80/443:
sudo ss -tlnp | grep -E ":80|:443" # The output shows the PID and process name (e.g., apache2) - Stop the competing web server process and disable its service so it doesn’t run at reboot:
sudo systemctl stop apache2 sudo systemctl disable apache2 - Start Nginx again:
sudo systemctl start nginx
- Find which process is currently occupying port 80/443:
2. PID File Missing or Corrupted #
- Symptoms: Running
nginx -s reloadproduces the errornginx: [error] open() "/run/nginx.pid" failed (2: No such file or directory). - Cause: Nginx isn’t currently running in the server memory, or the PID file was deliberately deleted by a temporary cleanup process.
- Solution: Run the start command cleanly instead of doing a reload:
sudo systemctl start nginx
Summary of Quick Mitigation Steps #
- Investigate 502 (Bad Gateway): Check whether our backend application process is dead. Use
curl -I localhost:PORTto make sure the backend port responds.- Investigate 504 (Gateway Timeout): Raise the
proxy_read_timeoutandfastcgi_read_timeoutdirectives in Nginx for heavy URLs, and optimize our backend database queries.- Investigate 403 (Forbidden): Check the alignment of file access permissions and ownership by the
www-datauser. Make sure the root directory has an index file likeindex.html.- Investigate 404 (Not Found): Check the physical directory path in the
rootdirective. Addtry_filesfor fallback routing on Single Page Applications (SPAs).- Use fullchain.pem: Always use the full chain certificate for the
ssl_certificatesetting to fend off broken browser trust chain errors.- Run nginx -t: Make it a habit to always validate the configuration file before reloading to prevent downtime on production servers.