How to Hide .html Extensions in URLs with Nginx Rewrite Rules


3 views

When serving static websites, having .html extensions in URLs creates several issues:

  • Breaks URL consistency when migrating from other web servers
  • Makes URLs look less clean and professional
  • Complicates URL sharing as users might manually remove extensions

Instead of creating multiple location blocks, we can handle this with a single rewrite rule in the server configuration:

server {
    listen 80;
    server_name foo.com;
    root /srv/www/foo/public_html;
    
    index index.html;

    location / {
        try_files $uri $uri.html $uri/ =404;
    }
    
    # Handle requests that explicitly end with .html
    location ~ \.html$ {
        rewrite ^/(.*)\.html$ /$1 permanent;
    }
}

The magic happens in two parts:

  1. try_files checks for the URI in this order:
    • Exact file match
    • Same path with .html appended
    • Directory index
    • 404 if none found
  2. The rewrite rule permanently redirects any .html URLs to their extension-less versions

For more complex scenarios, consider these additions:

# Ensure no directory listing
location ~ /$ {
    try_files $uri $uri.html =404;
}

# Handle legacy URLs with .htm extension
location ~ \.htm$ {
    rewrite ^/(.*)\.htm$ /$1 permanent;
}

# Proper content-type for HTML files
location ~* \.html?$ {
    add_header Content-Type text/html;
}

Always verify your changes:

  1. sudo nginx -t (tests configuration syntax)
  2. sudo systemctl reload nginx
  3. Test with curl: curl -I http://foo.com/bar

When serving static websites through Nginx, having .html extensions in URLs creates several issues:

  • Breaks consistent URL patterns when mixing dynamic and static content
  • Exposes implementation details to end users
  • Makes URLs longer and less memorable

The current approach using location blocks has significant limitations:


# Bad approach - requires manual maintenance
location /bar1 {
    alias /srv/www/foo/public_html/;
    index bar1.html;
}

This becomes unmanageable with:

  • Hundreds of HTML files
  • Frequent content updates
  • Team collaboration needs

Here's the proper Nginx configuration to handle this automatically:


server {
    listen 80;
    server_name foo.com;
    root /srv/www/foo/public_html;
    
    # Core rewrite rule
    location / {
        try_files $uri $uri.html $uri/ =404;
    }
    
    # Handle requests that end with .html
    if ($request_uri ~ ^/(.*)\.html$) {
        return 301 /$1;
    }
    
    # Prevent direct access to .html files
    location ~ \.html$ {
        internal;
    }
}

The configuration implements several key features:

  • Extensionless URLs: try_files checks for files with .html extension
  • Redirects: Any .html requests get 301 redirected
  • Security: Direct access to .html files is blocked

For more complex scenarios, consider these additions:


# Handle index files properly
index index.html;

# Cache control for static assets
location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
    expires 30d;
    add_header Cache-Control "public";
}

# Custom error pages
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;

Always validate your Nginx changes:


# Test configuration syntax
sudo nginx -t

# Reload configuration
sudo systemctl reload nginx

Watch out for these potential problems:

  • 403 Forbidden errors - check directory permissions
  • Infinite redirects - ensure your rewrite logic is sound
  • MIME type issues - verify your Content-Type headers