15

I need to write a rewrite rule for Nginx so that if a user tries to go to an old image url:

/images/path/to/image.png

and the file doesnt exist, try to redirect to:

/website_images/path/to/image.png

ONLY if the image exists in the new URL, otherwise continue with the 404. The version of Nginx on our host doesn't have try_files yet.

Jose Fernandez
  • 165
  • 1
  • 2
  • 6

3 Answers3

20
location /images/ {
    if (-f $request_filename) {
        break;
    }

    rewrite ^/images/(.*) /new_images/$1 permanent;
}

Though, you might want to bug your host to upgrade or find a better host.

Martin Fjordvald
  • 7,749
  • 1
  • 30
  • 35
  • This will redirect to /new_images on every 404 right? I don't want to do the rewrite unless I know the new_images file exists – Jose Fernandez Mar 16 '10 at 13:58
  • This checks to see if the file exists, and if that test fails, it redirects to new_images. What happens after that is not specified here. – tylerl Jul 31 '10 at 07:58
  • I'm afraid that your option is considered as common pitfalls by nginx authors https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/#check-if-file-exists – riverfall Jan 22 '18 at 10:40
  • 2
    @riverfall Heh, I wrote part of that page. My answer was written 8 years ago so yes it's a wee bit outdated, but the original question did specifically state that their host didn't provide a recent version of nginx so they didn't have access to try_files. – Martin Fjordvald Jan 24 '18 at 08:29
  • @MartinFjordvald a lot of "maintenance page" tutorials recommend using a "if file exists" on some maintenance html file for easy switching, is it an ok pattern (performance impact of checking if a file exists is my main concern)? If not, what do you recommend? – Guillaume86 Aug 28 '19 at 15:33
  • 1
    @Guillaume86 I wouldn't worry about that, an if + rewrite is totally safe and a single STAT check on a file is very light weight. It's much faster than going to a backend application to check for maintenance mode. – Martin Fjordvald Aug 30 '19 at 18:37
9

Please don't use if inside a location block. Bad things may happen.

location ~* ^/images/(.+)$ {
    root /www;
    try_files /path/to/$1 /website_images/path_to/$1 /any/dir/$1 @your404;
}

$1 becomes the filename to try in the try_files directive, which is made for what you're trying to accomplish.

This, OR, just rewrite it without checking. If that image isn't there, you'll get a 404 anyway. Honestly if you don't have try_files, the solution should probably be upgrading nginx to the latest stable branch.

  • 1
    While this answer _strictly_ does not answer the question ("[no] `try_files` yet"), for future visitors coming here, this answer deserves more up-votes. – DerMike Apr 18 '19 at 08:51
5

You could use something like this (untested for your specific case):

location ^/images/(?<imgpath>.*)$ {

    set $no_old  0;
    set $yes_new 0;

    if (!-f $request_filename)
    {
        set $no_old 1;
    }

    if (-f ~* "^/new_path/$imgpath")
    {
        set $yes_new 1$no_old;
    }

    # replacement exists in the new path
    if ($yes_new = 11)
    {
        rewrite ^/images/(.*)$ /new_path/$1 permanent;
    }

    # no image in the new path!
    if ($yes_new = 01)
    {
        return 404;
    }
}

Which is basically an alternative way of writing nested if statements, since you cannot nest in Nginx. See here for official reference on this "hack".

tmslnz
  • 201
  • 2
  • 5