2

is there a way in nginx to do something based on if the request body has a string or not? I am sure i can do it with using Lua module.. I am try to find out if there is a way using nginx alone.

I am hoping something like below will work.

 location /students-api {
    if ($request_body ~* "(.*)specialstudent(.*)" ) {
      set  $student_status  'special';
    }
  // and use student_status for some logic
 } 
Janier
  • 3,982
  • 9
  • 43
  • 96

1 Answers1

4

I think it should work, however it needs to be tested. In practice I used $request_body only for logging, not sure if it is available at the rewrite stage of request processing. Here is an official description which says:

The variable’s value is made available in locations processed by the proxy_pass, fastcgi_pass, uwsgi_pass, and scgi_pass directives when the request body was read to a memory buffer.

Additionally, you don't need those capture groups to check a variable for substring presence if you don't use them later (in fact you just wasting resources to keep them in memory), just if ($request_body ~* "specialstudent") { ... } should be enough.

Update

Here is another approach that has more chances to work since proxy_add_header directive is definitely executed later than the rewrite stage of request processing:

map $request_body $special {
    ~*"specialstudent"    "special";
    # otherwise '$special' variable value will be empty
}
server {
    ...
    location /students-api {
        ...
        proxy_set_header X-Student-Status $special;
        ...
    }
}

Update 2

After testing all of this, I can confirm that the if approach does not work:

server {
    ...
    location /students-api {
        if ($request_body ~* "specialstudent") {
            set $student_status "special";
        }
        proxy_set_header X-Student-Status $student_status;
        ...
    }
}

As being expected, the $request_body variable doesn't get initialized at the rewrite stage of request processing. However, the map approach work as expected:

map $request_body $student_status {
    ~*"specialstudent"    "special";
    # otherwise '$special' variable value will be empty
}
server {
    ...
    location /students-api {
        proxy_set_header X-Student-Status $student_status;
        ...
    }
}

What really surprises me is that the following example doesn't set any of two headers:

map $request_body $student_status {
    ~*"specialstudent"    "special";
    # otherwise '$special' variable value will be empty
}
server {
    ...
    location /students-api {
        if ($request_body ~* "specialstudent") {
            set $student_special "special";
        }
        proxy_set_header X-Student-Status $student_status;
        proxy_set_header X-Student-Special $student_special;
        ...
    }
}

Somehow accessing the $request_body variable at the early rewrite stage of request processing leads the map translation to stop working too. I didn't have an explanation of this behavior for now and would be grateful if someone could explain what happened here.

Update 3

I think I'm finally found an explanation of what happened with the last example in the Nginx Tutorials written by Yichun Zhang, the author of famous lua-nginx-module and the OpenResty bundle:

Some Nginx variables choose to use their value containers as a data cache when the "get handler" is configured. In this setting, the "get handler" is run only once, i.e., at the first time the variable is read, which reduces overhead when the variable is read multiple times during its lifetime.

Looks like the $request_body variable behaves exactly this way, if being accessed at the early NGX_HTTP_REWRITE_PHASE (see the request processing phases description). Its value, if being read during that phase, gets cached as an empty value and became useless during the later request processing phases.

Ivan Shatsky
  • 13,267
  • 2
  • 21
  • 37
  • Hm..I have logics based on this..thats the reason.. I tried that if check ;but didnt work – Janier Oct 22 '20 at 18:14
  • Different nginx directives processed at different request processing stages. Maybe you could show some more of this location config? Does this location contains some request to the backend (proxy/FCGI/WSGI)? – Ivan Shatsky Oct 22 '20 at 18:24
  • yes.. then its being set to the header and the request is being proxied... all the logic works without this If ;now i want to isolate the conidition and move into the if block – Janier Oct 22 '20 at 18:53
  • Something like `proxy_set_header X-Student-Status $special;`? – Ivan Shatsky Oct 22 '20 at 18:55
  • Exactly.Thats what I am doing..So if the request body contains a value , set this header..thats what the logic.. – Janier Oct 22 '20 at 18:57
  • As far as I understand, it *should* work that way. Your requests are POST requests, right? Ok, this one interested me, I'll give it a test on my sandbox when I have a couple of free time and let you know. – Ivan Shatsky Oct 22 '20 at 19:00
  • yeah..post request with a json body if that makes any difference...thanks a lot ..appreciate it – Janier Oct 22 '20 at 19:02
  • One more question, can those bodies size exceed 16 Kb or they are quite small? – Ivan Shatsky Oct 22 '20 at 19:16
  • Nope.. I thought of it..this will be just a small json – Janier Oct 22 '20 at 19:18
  • Can you test one more approach? Didn't test the first one yet, but this one has much more chances to work. – Ivan Shatsky Oct 22 '20 at 23:11
  • let me have a look at the map $requestbody – Janier Oct 23 '20 at 01:37
  • Finished testing, see the results in the updated answer. – Ivan Shatsky Oct 23 '20 at 11:26
  • Any success? Any feedback? – Ivan Shatsky Oct 24 '20 at 10:27
  • Hey..sorry for the late response.. the map approach is pretty good ... only thing i am stuggling with , if the $request_body doesnt have "specialstudent" , the header shouldnt be set all... it shouldnt set the header at all if the value is not there – Janier Oct 26 '20 at 19:49
  • but otherwise , map is the best way to do it..Thanks..its helpful alot – Janier Oct 26 '20 at 19:59
  • 1
    It will work exactly that way, if the body won't contain "specialstudent" string, the `$student_status` variable would have an empty value and the header won't be set at all (since we are passing an empty value to the `proxy_set_header` directive). – Ivan Shatsky Oct 26 '20 at 21:49