Afiku
Afiku

Reputation: 251

Rewrite query string to path params

I have the following configuration of nginx that hosts my image service:

    upstream thumbor {
        server localhost:8888;
    }

    server {
        listen       80;
        server_name  my.imageserver.com;
        client_max_body_size 10M;
        rewrite_log on;
        location ~ /images { 
            if ($arg_width="10"){
                rewrite ^/images(/.*)$ /unsafe/$1 last;
            }
            rewrite ^/images(/.*)$ /unsafe/$1 last;
        }
        location ~ /unsafe {
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header HOST $http_host;
            proxy_set_header X-NginX-Proxy true;

            proxy_pass http://thumbor;
            proxy_redirect off;
        }

        location = /favicon.ico {
            return 204;
            access_log     off;
            log_not_found  off;
        }
    }

I am trying to rewrite the following urls:

from

http://my.imageserver.com/images/Jenesis/EmbeddedImage/image/jpeg/jpeg/9f5d124d-068d-43a4-92c0-1c044584c54a.jpeg

to

http://my.imageserver.com/unsafe/Jenesis/EmbeddedImage/image/jpeg/jpeg/9f5d124d-068d-43a4-92c0-1c044584c54a.jpeg

which is quite easy, the problem begins when I want to allow query string that should go to the path of the url like so:

from

http://my.imageserver.com/images/Jenesis/EmbeddedImage/image/jpeg/jpeg/9f5d124d-068d-43a4-92c0-1c044584c54a.jpeg?width=150&height=200&mode=smart

to

http://my.imageserver.com/unsafe/150x200/smart/Jenesis/EmbeddedImage/image/jpeg/jpeg/9f5d124d-068d-43a4-92c0-1c044584c54a.jpeg

Also it will be better if the order of the query strings won't matter.

I tried using: $arg_width but it didn't seem to work.

Using nginx 1.6.1 on ubuntu.

Help would be much much appreciated.

Upvotes: 2

Views: 9534

Answers (3)

Dayo
Dayo

Reputation: 12775

You need as scripting option that can evaluate the input and construct the rewrite URL. I use the third party Nginx Lua Module for such logic.

You will need to compile the module into Nginx after installing Lua or preferably, LuaJit on your machine.

Alternatively, you can install Openresty which is Nginx bundled with a few modules that completely transforms the Nginx into a full web application server. Openresty will take care of Lua/LuaJit dependencies during installation.

If you have this in place, this should do the job for you:

upstream thumbor {
    server localhost:8888;
}
server {
    [...]
    location ~ ^/images { 
        rewrite_by_lua '
            -- Use local variables to limit their scope to this location block
            local i, j, key, val, args, dimension, modetype, tempheight, replacement

            -- We only go ahead if there are request arguments to start with
            if ngx.var.is_args then
                -- Get the request arguments
                -- These are loaded into a Lua table of the form, { a = 1, b = 2, c = 3 } 
                args = ngx.req.get_uri_args()
                -- Loop through the args table and build our replacement string
                for key, val in pairs(args) do
                    if key == "width" then
                        if tempheight then
                            dimension = val .. "x" .. tempheight
                        else
                            dimension = val 
                        end
                    elseif key == "height" then 
                        if dimension then
                            dimension = dimension .. "x" .. val
                        else
                            tempheight = val 
                        end
                    elseif key == "mode" then 
                        modetype = val 
                    end
                end

                -- Construct the replacement string. 
                replacement = "/unsafe/" .. dimension .. "/" .. modetype .. "/"

                -- Replace "/images/" in the request url with the replacement string. 
                local newuri, n, err = ngx.re.sub(ngx.unescape_uri(ngx.var.request_uri), "/images/", replacement, "io")

                -- If there is a new string, then redirect to the new URL
                if newuri then
                    return ngx.redirect(newuri, 301) 
                end
            end
        ';
    }
    location ~ /unsafe {
        [...]
        proxy_pass http://thumbor;
    }
    [...] 
}

Upvotes: 2

Stefano Falsetto
Stefano Falsetto

Reputation: 572

You can use the arg_name parameter, and you can get rid of location block:

 rewrite ^/images/(.*)$ /unsafe/$1;
 # WARNING: Here we suppose that when we have a "mode" parameter, we have
 # width and height paremeters too
 if ($arg_mode) {
     rewrite ^/unsafe/(.*)$ /images/unsafe/smart/$arg_mode/${arg_width}x${arg_height}/$1 last;
 }

Upvotes: 2

regilero
regilero

Reputation: 30496

Working with query arguments is always hard (this is also true with Apache).

But in your example when you do:

location ~ /images { 
    if ($arg_width="10"){
        rewrite ^/images(/.*)$ /unsafe/$1 last;
    }
    rewrite ^/images(/.*)$ /unsafe/$1 last;
}

I do not see any difference between the 2 rewrites... so that's maybe why it is not working.

Anyway you could maybe try something like that (based on this thread):

location ^~ /images {
    # get there only if we have a query string
    if ($is_args) {
        set $width ""; 
        set $height "";
        if ($args ~* "(?:^|&)width=([^&]+)") { 
            set $width $1; 
        }
        if ($args ~* "(?:^|&)height=([^&]+)") { 
            set $height $1; 
        }

        # string concatenation using sort of bash syntax
        set $dim "${width}x${height}";
        # maybe we should add a control here on $dim !='x'...

        # the ? here prevent query string from being appended
        rewrite ^/images(/.*)$ /unsafe/$dim/$1? last;
    }
    rewrite ^/images(/.*)$ /unsafe/$1 last;
} 
location ~ /unsafe {
    (...)

Upvotes: 4

Related Questions