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


4 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