Nginx try_files Configuration for HTML Extension Removal and Clean URL Rewriting


2 views
nginx: [emerg] "try_files" directive "duplicate default parameter" in /etc/nginx/conf.d/mysite.conf:12

Your current configuration attempts to serve both the directory and index.html file simultaneously, which creates ambiguity. The 500 error occurs because nginx can't properly resolve the request path when both $uri/ and $uri/index.html are specified in try_files.

Here's the improved configuration that properly handles both HTML extension removal and clean URL rewriting:

server {
    listen       80;
    server_name  mysite.com;
    root         /var/www/mysite/public;

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

    location /media/ {
        alias /var/www/mysite/public/media/;
        error_page 404 = /404;
        expires 30d;
    }

    location /static/ {
        alias /var/www/mysite/public/static/;
        error_page 404 = /404;
        expires 30d;
    }
}

The revised configuration makes several important changes:

  • Moved root directive outside location blocks for cleaner configuration
  • Modified try_files order to properly check for both .html files and directory indexes
  • Added proper error handling with =404 fallback

Here's how different URLs will now resolve:

http://mysite.com/ → /var/www/mysite/public/index.html
http://mysite.com/post/13considerations → /var/www/mysite/public/post/13considerations.html
http://mysite.com/post/13considerations/ → /var/www/mysite/public/post/13considerations/index.html

For more complex scenarios where you might have both HTML and non-HTML files:

location / {
    try_files $uri $uri/ @htmlext;
}

location @htmlext {
    if (-f "${document_root}${uri}.html") {
        rewrite ^(.*)$ $1.html break;
    }
    try_files $uri $uri/ =404;
}

This configuration first checks for the exact file, then directory, then falls back to checking for .html extension before finally returning 404 if nothing is found.


When working with static HTML sites in Nginx, we often want to achieve clean URLs that don't show the .html extension. The directory structure in question looks like this:

public
|-- index.html
|-- media
|   -- blurred_image-8.jpg
|-- post
|   -- 13considerations
|       -- index.html

The original Nginx configuration attempts to handle this with:

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

This setup works for the root path (/) but fails with 500 errors for nested paths like /post/13considerations/. The issue lies in how try_files handles directory structures.

Here's the correct configuration that handles both root and nested paths:

server {
    listen       80;
    server_name  mysite.com;

    root /var/www/mysite/public;

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

    location /media/ {
        alias /var/www/mysite/public/media/;
        error_page 404 = /404;
        expires 30d;
    }

    location /static/ {
        alias /var/www/mysite/public/static/;
        error_page 404 = /404;
        expires 30d;
    }
}

The critical changes are:

  • Moved root directive to server level for consistency
  • Modified try_files to check for .html extension explicitly
  • Added proper fallback to 404 when no file is found
  • Maintained separate handling for static assets

This configuration will now properly handle:

  • http://mysite.com/ → serves index.html
  • http://mysite.com/post/13considerations → serves /post/13considerations/index.html
  • http://mysite.com/about → serves /about.html if exists

For better SEO and user experience, you might want to:

# Redirect .html requests to clean URLs
if ($request_uri ~ ^/(.*)\.html$) {
    return 301 /$1;
}

# Ensure trailing slash for directories
if (-d $request_filename) {
    rewrite [^/]$ $uri/ permanent;
}