Nginx Configuration: Fixing Root Path (/) Not Serving index.html Properly


5 views

After analyzing your configuration, the core issue stems from the proxy_pass directive in your root location block. When you have both:

location / {
  proxy_pass http://127.0.0.1:3010;
  # ... other proxy settings
}

AND you want to serve static files from the same path, Nginx gives priority to the proxy configuration. The static files location block never gets executed for root requests.

The most straightforward fix is to create a dedicated location for exactly "/":

location = / {
  try_files /index.html =404;
}

location / {
  proxy_pass http://127.0.0.1:3010;
  # ... keep existing proxy settings
}

Alternatively, modify your existing static files block to handle HTML files first:

location ~* \.(html|htm)$ {
  try_files $uri /index.html;
  expires -1;
  add_header Cache-Control "no-store";
}

location / {
  try_files $uri @proxy;
}

location @proxy {
  proxy_pass http://127.0.0.1:3010;
  # ... proxy settings
}

After making changes:

  • Clear browser cache completely (or use incognito)
  • Test with curl -I http://yourserver/ to verify headers
  • Check error logs: tail -f /home/logs/error.log

Here's a proven configuration that handles both proxy and static files:

server {
  root /home/www/public;
  
  location = / {
    try_files /index.html =404;
  }

  location / {
    try_files $uri @node_proxy;
  }

  location @node_proxy {
    proxy_pass http://127.0.0.1:3010;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection 'upgrade';
    proxy_set_header Host $host;
    proxy_cache_bypass $http_upgrade;
  }

  location ~* \.(?:ico|css|js|gif|jpe?g|png|woff2?|eot|svg|ttf|html)$ {
    expires max;
    add_header Cache-Control "public";
    try_files $uri =404;
  }
}

When working with Nginx, a common frustration occurs when requests to the root path (/) don't automatically serve index.html as expected. This typically happens when:

  • Proxy configurations override default document handling
  • Location blocks interfere with default index processing
  • Multiple index directives conflict with each other

In your current setup, the issue stems from several configuration elements working against each other:

location / {
    index index.html index.htm;
    proxy_pass http://127.0.0.1:3010;
    break;
}

The break directive here is particularly problematic as it prevents Nginx from falling back to static file handling after the proxy attempt fails.

Here's a corrected version that properly handles both static files and proxy requests:

server {
    root /home/www/public;
    listen 80;
    server_name localhost;
    
    # First try to serve static files
    location / {
        try_files $uri $uri/ /index.html;
    }

    # Proxy API requests
    location /api/ {
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-NginX-Proxy true;
        proxy_pass http://127.0.0.1:3010/;
        proxy_redirect off;
    }

    # Static content cache headers
    location ~* \.(?:ico|css|js|gif|jpe?g|png|html)$ {
        expires 30d;
        access_log off;
        add_header Cache-Control "public";
    }
}

When combining static and proxy content in Nginx:

  • Order matters: More specific locations should come before catch-all locations
  • try_files is your friend: It provides fallback mechanisms for missing files
  • Isolate proxy paths: Keep proxy rules in their own location blocks
  • Watch for break/try_files interaction: These directives often conflict

Always verify your changes with:

nginx -t && nginx -s reload

Then test with curl to ensure proper behavior:

curl -I http://localhost/

Should return 200 status code with your index.html content.

If issues persist:

  1. Check error logs: tail -f /home/logs/error.log
  2. Enable debug logging temporarily
  3. Verify file permissions on your static files
  4. Check for case sensitivity in filenames