You made a quick change to your Nginx configuration, ran nginx -t to test it, and got an error. The message isn't always clear about what's wrong, and you're not sure if it's a syntax error, missing semicolon, or something else entirely. Let's systematically debug Nginx configuration errors.

Understanding nginx -t Output

When configuration has errors, nginx -t shows:

bash
nginx: [emerg] unknown directive "server_name" in /etc/nginx/sites-enabled/example.com:12
nginx: configuration file /etc/nginx/nginx.conf test failed

Or sometimes:

bash
nginx: [emerg] unexpected "}" in /etc/nginx/sites-enabled/example.com:25
nginx: configuration file /etc/nginx/nginx.conf test failed

The severity levels are: - emerg - Fatal error, Nginx won't start - alert - Serious issue, needs fixing - crit - Critical, will cause problems - error - Error condition - warn - Warning, might work but should fix

Step 1: Get Detailed Error Location

The error message tells you the file and line number. Let's get more context:

```bash # Basic test nginx -t

# More verbose output nginx -T 2>&1 | less

# Show line numbers in config cat -n /etc/nginx/sites-enabled/example.com | sed -n '10,30p' ```

If the error mentions line 25, view lines around it:

bash
sed -n '20,30p' /etc/nginx/sites-enabled/example.com

Step 2: Fix Missing Semicolons

The most common error. Nginx requires semicolons after every directive:

Error: `` nginx: [emerg] unexpected "}" in /etc/nginx/sites-enabled/example.com:10

Broken config: ``nginx server { listen 80 server_name example.com # Missing semicolon! }

Fixed: ``nginx server { listen 80; server_name example.com; }

The error points to the line after the missing semicolon. Check the preceding lines.

Step 3: Fix Unknown Directive Errors

Error: `` nginx: [emerg] unknown directive "server_name" in /etc/nginx/sites-enabled/example.com:5

This usually means the directive is in the wrong context:

Broken config: ```nginx http { server_name example.com; # Wrong! server_name must be in server block

server { listen 80; } } ```

Fixed: ``nginx http { server { listen 80; server_name example.com; } }

Check directive context in documentation: ```bash # Check if module is loaded nginx -V 2>&1 | grep -o "with-[^ ]*" | tr ' ' '\n'

# Or check loaded modules ls /etc/nginx/modules-enabled/ ```

Step 4: Fix Brace Mismatch

Unmatched braces cause confusing errors:

Error: `` nginx: [emerg] unexpected end of file, expecting "}" in /etc/nginx/nginx.conf:85

Or: `` nginx: [emerg] unexpected "}" in /etc/nginx/sites-enabled/example.com:25

Debug technique: ```bash # Count opening and closing braces grep -o '{' /etc/nginx/nginx.conf | wc -l grep -o '}' /etc/nginx/nginx.conf | wc -l

# They should be equal ```

Visual debugging with indentation: ```bash # Indent config to see structure apt-get install indent # Debian/Ubuntu # or yum install indent # RHEL/CentOS

indent -linux -i4 /etc/nginx/sites-enabled/example.com ```

Manual check: ``nginx http { server { location / { # content } # closes location } # closes server } # closes http

Step 5: Fix Include Errors

Error: `` nginx: [emerg] open() "/etc/nginx/sites-enabled/missing-file.conf" failed (2: No such file or directory)

Check includes:

```bash # Find all include statements grep -r "include" /etc/nginx/nginx.conf

# Check if files exist ls -la /etc/nginx/sites-enabled/ ls -la /etc/nginx/conf.d/ ```

Fix by either: - Creating the missing file - Removing the include statement - Commenting out the include

Step 6: Fix Port Binding Issues

Error: `` nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)

Check what's using the port:

bash
# Find process on port 80
ss -tlnp | grep :80
netstat -tlnp | grep :80
lsof -i :80

If another process is using the port:

```bash # Identify the process ps aux | grep $(lsof -t -i:80)

# Stop the conflicting service systemctl stop apache2 # if Apache is running # or systemctl stop httpd ```

If Nginx itself has a duplicate:

```bash # Check all listen directives grep -r "listen 80" /etc/nginx/

# Check for default server conflicts grep -r "default_server" /etc/nginx/ ```

Only one default_server per port is allowed.

Step 7: Fix SSL Certificate Errors

Error: `` nginx: [emerg] cannot load certificate "/etc/nginx/ssl/missing.crt": BIO_new_file() failed (SSL: error:02001002:system library:fopen:No such file or directory)

Or: `` nginx: [emerg] SSL_CTX_load_verify_locations("/etc/nginx/ssl/cert.crt") failed (SSL: error:02001002:system library:fopen:No such file or directory)

Check certificate files:

```bash # Check files exist ls -la /etc/nginx/ssl/

# Check paths in config grep -r "ssl_certificate" /etc/nginx/

# Verify certificates openssl x509 -in /etc/nginx/ssl/example.com.crt -text -noout ```

Common mistakes:

```nginx # Wrong: relative path ssl_certificate ssl/example.com.crt;

# Correct: absolute path ssl_certificate /etc/nginx/ssl/example.com.crt;

# Wrong: swapped cert and key ssl_certificate /etc/nginx/ssl/example.com.key; ssl_certificate_key /etc/nginx/ssl/example.com.crt;

# Correct ssl_certificate /etc/nginx/ssl/example.com.crt; ssl_certificate_key /etc/nginx/ssl/example.com.key; ```

Step 8: Fix Upstream Errors

Error: `` nginx: [emerg] host not found in upstream "backend" in /etc/nginx/conf.d/upstream.conf:5

Broken config: ``nginx upstream backend { server backend-server:3000; # DNS can't resolve at startup }

Solutions:

  1. 1.Use IP address:
  2. 2.```nginx
  3. 3.upstream backend {
  4. 4.server 192.168.1.100:3000;
  5. 5.}
  6. 6.`
  7. 7.Use a resolver for dynamic DNS:
  8. 8.```nginx
  9. 9.resolver 8.8.8.8;

location / { set $upstream http://backend-server:3000; proxy_pass $upstream; } ```

  1. 1.Ensure hosts file or DNS is configured:
  2. 2.```bash
  3. 3.# Check resolution
  4. 4.getent hosts backend-server
  5. 5.ping backend-server
  6. 6.`

Step 9: Fix Variable and Map Errors

Error: `` nginx: [emerg] unknown variable "$upstream_addr" in /etc/nginx/nginx.conf:45

Check if variable exists in the correct context:

nginx
# Some variables only exist in certain contexts
log_format main '$remote_addr - $upstream_addr';  # $upstream_addr only exists after proxy_pass

Map directive errors: ``nginx # Broken: missing default map $uri $myvar { /api backend1; /web backend2; # missing default }

Fixed: ``nginx map $uri $myvar { default backend_default; /api backend1; /web backend2; }

Step 10: Debug Complex Configurations

For complex configs with many includes:

```bash # Dump complete parsed configuration nginx -T 2>&1 | less

# Find the actual problematic line nginx -T 2>&1 | grep -A 5 -B 5 "problem_text"

# Check configuration hierarchy find /etc/nginx -name "*.conf" -exec echo "=== {} ===" \; -exec cat {} \; ```

Validate step by step:

```bash # Test only main config nginx -t -c /etc/nginx/nginx.conf

# Temporarily disable all includes mkdir /etc/nginx/sites-disabled mv /etc/nginx/sites-enabled/* /etc/nginx/sites-disabled/ nginx -t

# Add back one by one mv /etc/nginx/sites-disabled/default /etc/nginx/sites-enabled/ nginx -t # Repeat until you find the bad file ```

Common Error Patterns Quick Reference

Error MessageLikely CauseFix
unexpected "}"Missing semicolon aboveAdd semicolon
unexpected end of fileMissing closing braceCount braces, add missing
unknown directiveWrong context or missing moduleMove to correct block or install module
directive is not allowedWrong nesting levelCheck directive documentation
bind() failedPort in useStop conflicting service or change port
no such file or directoryMissing include or SSL fileCreate file or fix path
host not found in upstreamDNS resolution failureUse IP or fix DNS
permission deniedConfig file permissionschmod 644 and chown root:root

Verification Process

After fixing:

```bash # 1. Test syntax nginx -t

# 2. Show parsed config (verify changes) nginx -T | less

# 3. Reload if test passes nginx -t && systemctl reload nginx

# 4. Check status systemctl status nginx

# 5. Verify listening ss -tlnp | grep nginx ```

Prevent Configuration Errors

Create a pre-commit hook:

bash
#!/bin/bash
# .git/hooks/pre-commit
nginx -t
if [ $? -ne 0 ]; then
    echo "Nginx configuration test failed. Commit aborted."
    exit 1
fi

Or use a CI check:

yaml
# .github/workflows/nginx-lint.yml
name: Nginx Lint
on: [push]
jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Install Nginx
        run: sudo apt-get install nginx
      - name: Copy configs
        run: sudo cp -r nginx/* /etc/nginx/
      - name: Test config
        run: sudo nginx -t

Configuration errors are almost always syntax mistakes—missing semicolons, mismatched braces, or wrong contexts. When in doubt, strip the config down to basics and add pieces back one at a time.