5

I am building an application where I need to do some analytics on the api-data combination usage. Below is my nginx configuration -

location /r/ {
    rewrite /r/(.*)$ http://localhost:3000/sample/route1/$1 redirect;
    post_action /aftersampleroute1/$1;
}
location /aftersampleroute1/ {
    rewrite /aftersampleroute1/(.*) /stats/$1;
    proxy_pass http://127.0.0.1:3000;
}

location /r/ is used to redirect the browser request http://localhost:80/r/quwjDP4us to api /sample/route1/quwjDP4us which uses the id quwjDP4us to do something. Now in the background I want to pass the id quwjDP4us to an stats api /stats/quwjDP4us which updates the db record for that id.

When I start nginx and make the request http://localhost:80/r/quwjDP4us nginx successfully redirects my request to my application but doesn't make the 2nd request in the background to the stats api. What am I missing?

Note - post_action is not included in nginx docs, is there an alternate module/directive I can use?

Alexander Azarov
  • 12,971
  • 2
  • 50
  • 54
John Doe
  • 157
  • 1
  • 10

1 Answers1

6

As you correctly mentioned, post_action is not documented and has always been considered an unofficial directive.

Nginx provides a new "mirror" module since version 1.13.4, described here in the documentation. So I advise you to give it a try. In your case, it would look like this –

location /r/ {
    rewrite /r/(.*)$ http://localhost:3000/sample/route1/$1 redirect;
    mirror /stats;
}

location = /stats {
    internal;
    rewrite /sample/route1/(.*) /stats/$1;
    proxy_pass http://127.0.0.1:3000;
}

This will not work!

I've built a test configuration and unfortunately this will not work. It works for neither rewrite nor return. But it works for proxy_pass.

Why

The explanation follows. A HTTP request sequentially passes few "phases" during processing in Nginx. The thing is that mirror gets triggered in phase PRECONNECT which occurs later than phase REWRITE where rewrite/return end request processing. So, mirror does not even get triggered because its processing would happen later.

In case of serving files from the location or proxying via proxy_pass (or fastcgi_pass, etc), the processing will finally reach REWRITE phase and mirror will be executed.

Phases are described in Nginx documentation here.

Workarounds

I do not see any good solution without trade-offs. You could create an extra location (returning a redirect) and proxy your request from /r/, so that mirror gets triggered. Something like this, depending on the rest of your configuration:

location /r/ {
  # you may need setting Host to match `server_name` to make sure the
  # request will be caught by this `server`.
  # proxy_set_header Host $server_name;
  proxy_pass http://<ip from listen>:<port from listen>/redirect/;
  mirror /stats;
}

location = /redirect {
  rewrite /redirect(.*)$ http://localhost:3000/sample/route1$1 redirect;
}

Certainly this is suboptimal and has extra boilerplate.

Alexander Azarov
  • 12,971
  • 2
  • 50
  • 54
  • 1
    Didn't work. `rewrite /r/(.*)$ http://localhost:3000/sample/route1/$1 redirect;` I think the redirect flag is redirecting the request and the mirror doesn't get executed. – John Doe Oct 02 '18 at 17:01
  • 1
    @JohnDoe Unfortunately, you are right. I've created a test configuration and it doesn't work. I shall update my answer. – Alexander Azarov Oct 02 '18 at 18:40
  • @JohnDoe I've reached Nginx maintainers and asked them about this issue. If you are curious to read it in Russian , the conversation is [here](https://mailman.nginx.org/pipermail/nginx-ru/2018-October/061531.html). I have updated my answer with the information I've got. – Alexander Azarov Oct 03 '18 at 07:22