Nginx Rewrite Rule: Redirect to Alternate Image Path Only If File Exists


13 views

When migrating image storage locations in Nginx, we often need to handle legacy URL patterns while ensuring proper fallback behavior. The specific requirement is:

  • Check if requested image exists at original path (/images/...)
  • If not found, verify existence at new path (/website_images/...)
  • Only redirect when the new path exists, otherwise return 404

For Nginx versions without try_files support, we can use this configuration:

location ~* ^/images/(.+)\.(png|jpg|jpeg|gif)$ {
    # First check if file exists in original location
    if (-f $document_root/images/$1.$2) {
        break;
    }
    
    # Then check new location
    if (-f $document_root/website_images/$1.$2) {
        rewrite ^/images/(.*)$ /website_images/$1 permanent;
    }
    
    # Else will return 404 automatically
}

Since this performs multiple filesystem checks, consider these optimizations:

  1. Add regex patterns to only match image extensions
  2. Implement caching if you have many redirects
  3. Set proper expires headers for redirected assets

For large-scale implementations, Nginx map feature can be more efficient:

map $uri $new_uri {
    ~^/images/(.*) /website_images/$1;
}

server {
    ...
    location /images/ {
        if (-f $document_root$new_uri) {
            return 301 $new_uri;
        }
        try_files $uri =404;
    }
}

Always verify your configuration with:

  • nginx -t for syntax validation
  • Actual requests with curl -I to check headers
  • Error log monitoring during rollout

When migrating image storage locations in web applications, we often need to handle legacy URLs while ensuring proper fallback behavior. Here's a common case:


# Old image path (might not exist)
/images/products/123.png

# New image path (might exist)
/website_images/products/123.png

We want Nginx to:

  1. First check if /images/... exists
  2. If not, check if /website_images/... exists
  3. Only rewrite if the new location exists
  4. Fall back to 404 if neither exists

For Nginx versions without try_files support, we can use rewrite with file checks:


location /images/ {
    # Check if original file exists
    if (-f $request_filename) {
        break;
    }
    
    # Capture the path after /images/
    if ($uri ~ ^/images/(.*)$) {
        set $image_path $1;
        
        # Check if new location exists
        if (-f $document_root/website_images/$image_path) {
            rewrite ^/images/(.*)$ /website_images/$1 last;
        }
    }
    
    # Fall through to 404 if no matches
}

Here's a full server block implementation:


server {
    listen 80;
    server_name example.com;
    root /var/www/html;
    
    location / {
        try_files $uri $uri/ =404;
    }
    
    location /images/ {
        # Check original file first
        if (-f $request_filename) {
            break;
        }
        
        # Extract path component
        if ($uri ~ ^/images/(.*)$) {
            set $image_path $1;
            set $new_path /website_images/$image_path;
            
            # Verify new location exists
            if (-f $document_root$new_path) {
                rewrite ^ $new_path last;
            }
        }
        
        # No matches - return 404
        return 404;
    }
}

For high-traffic sites, consider these optimizations:


# Map approach (more efficient than multiple ifs)
map $uri $new_image_path {
    ~^/images/(?.*)$ /website_images/$path;
}

server {
    # ...
    
    location /images/ {
        if (-f $request_filename) {
            break;
        }
        
        if ($new_image_path) {
            rewrite ^ $new_image_path last;
        }
        
        return 404;
    }
}

Verify with these test cases:


# Case 1: Old path exists
curl -I http://example.com/images/existing.png

# Case 2: Only new path exists
curl -I http://example.com/images/moved.png

# Case 3: Neither exists
curl -I http://example.com/images/nonexistent.png

Remember to reload Nginx after changes:

nginx -s reload