openresty / replace-filter-nginx-module

Streaming regular expression replacement in response bodies
260 stars 68 forks source link

Replace only on specific status response #13

Open theMiddleBlue opened 8 years ago

theMiddleBlue commented 8 years ago

Hi,

is possible to replace a string only when proxy_pass return a 404 stutus? I mean something like

proxy_pass http://foo.bar/nonexistent/

if ($status = "404") {
    replace_filter 'Hello' 'Ciao';
}

thank you in advance, best regards.

-theMiddle

agentzh commented 8 years ago

@theMiddleBlue "If is evil": https://www.nginx.com/resources/wiki/start/topics/depth/ifisevil/ Your example with NGINX's if directive will never work as you wish.

For your goal, you can use ngx_lua module's header_filter_by_lua* with ngx_replace_filter's replace_filter_skip directive. Consider the following self-contained example that has been tested on my side:

    location = /t {
        set $skip_filter '';

        proxy_pass http://127.0.0.1:$server_port/fake;

        header_filter_by_lua_block {
            if ngx.status ~= 404 then
                ngx.var.skip_filter = "1"
            end
        }

        replace_filter_skip $skip_filter;

        replace_filter 'Hello' 'Ciao';
    }

    location = /fake {
        default_type text/html;
        return 404 "Hello, World!";
    }

Accessing /t gives

Ciao, World!

If you replace the 404 number with 200 in location = /fake, then accessing /t gives

Hello, World!

You need to ensure ngx_lua's filter runs before ngx_replace_filter, by adding ngx_lua after ngx_replace_filter in your ./configure command-line while building your nginx. For example,

./configure --add-module=/path/to/replace-filter-nginx-module \
             --add-module=/path/to/lua-nginx-module
theMiddleBlue commented 8 years ago

Thank you very much @agentzh

i tried with the following configuration:

location ~* /.* {
   set $skip_filter '';
   proxy_pass mywebsite;

   header_filter_by_lua_block {
      if ngx.status ~= 404 then
         ngx.var.skip_filter = "1"
      end
   }

   replace_filter_skip $skip_filter;
   replace_filter '</html>' "</html><!-- $skip_filter -->";

It works but it replaces all requests (200 and 404). Probably, as you said, i need to ensure that ngx_lua's filter runs before ngx_replace_filter, but i'm using OpenResty... how can i do it? :) i've run ./configure in OpenResty like this:

./configure --add-module=/path/replace-filter-nginx-module
make
make install

When ngx.status is 404:

</html><!--  -->

When ngx.status is 200:

</html><!-- 1 -->
agentzh commented 8 years ago

@theMiddleBlue Oh, it's a bit tricky to adjust module order with OpenResty and in your setup, ngx_replace_filter's filter always runs before header_filter_by_lua which means the Lua conditional is too late to run. Maybe we should bundle the ngx_replace_filter into OpenResty directly so that OpenResty takes care of the module order automatically.

theMiddleBlue commented 8 years ago

@agentzh i've used the following configuration for make it works with OpenResty using body_filter_by_lua_block:

location ~* /.* {
   proxy_pass mywebsite;

   body_filter_by_lua_block {
      if ngx.status == 404 then
         local from,to,err = ngx.re.find(ngx.arg[1], '\\<\\/html\\>')
         if from then
            ngx.arg[1] = '<html><body><h1>404 Not Found</h1></body></html>'
            ngx.arg[2] = true
         end
      end
   }
}
gumpjt commented 7 years ago

Same problem. I also need to make sure ngx_lua's filter run first. I'm using OpenResty. Is there any way to change the order? BTW: I think all the module order problem should handled by some kind of tool or something.