Similar to the issue mentioned here https://forum.openresty.us/d/6503-get-content-of-second-set-cookie-header
I have an NGINX configuration that gets the cookies stored in Set-Cookie by the upstream auth_request and I need to return those set-cookie to the client, however whenever I try to return those cookies only the first set-cookie is returned to the client.
Below is an example configuration to demonstrate the issue
location /auth/ {
proxy_pass http://auth/;
proxy_pass_request_body off;
proxy_redirect off;
}
location / {
auth_request /auth/loggedin;
auth_request_set $auth_cookie $upstream_http_set_cookie;
add_header Set-Cookie $auth_cookie;
proxy_set_header Cookie "$http_cookie; $auth_cookie";
proxy_pass http://someservice/;
}
In my above example I expect that multiple cookies could be returned in a Set-Cookie header a=12; PATH:"/", b=2; PATH:/"
and I want to pass whatever set-cookies come from the upstream service to clients browser via add_header. Currently only the cookie a
is making it to the client and b
is always missing.
Note:
I want it to be generic and so I can’t grab the exact cookie names from a header.
Thank you for any help you can provide!
2
Answers
Unfortunately it is impossible to do it the way you want to. Setting cookies using multiple
Set-Cookie
header is a common approach, the MDN documentation onSet-Cookie
header explicitly says that:When multiple headers with the same name are received from the upstream, only the first one is accessible using
$upstream_http_<header_name>
variable (with a few exceptions, e.g.Cache-Control
one, if I remember correctly). There is a ticket for that on nginx bug tracker (although I did’t consider it a bug).Set-Cookie
header is really a special case that can’t be folded in opposite to many other headers, check this answer and comments below it to see why is it so. (Of course, you are still free to use any$upstream_cookie_<cookie_name>
per-cookie variable).However it is possible to do it using OpenResty (mentioned in your question) or
lua-nginx-module
. The bad news, it will be incompatible with theauth_request
directive since it is impossible to add thoselua_...
handlers to the auth location (or any other subrequest location that can be used, e.g., byadd_before_body
oradd_after_body
directives from thengx_http_addition_module
). You didn’t get an error, but those handlers won’t be fired on a subrequest. The good news, functionality similar toauth_request
can be implemented usingngx.location.capture
API call.So if using
nginx-lua-module
is suitable for you (and if it isn’t, maybe the solution will help some others), this can be done the following way:This code does not check possible cookie names intersection, or it will be much more complex. However I think (not checked this on practice) that it doesn’t really matter because even if a two
Set-Cookie
requests with the same cookie name but different values will be returned to the client, the last one will be used. This code makesSet-Cookie
requests from the auth server arriving after theSet-Cookie
requests from the main upstream. To do the opposite you’d need to change theline to the
Special thanks to the @wilsonzlin for the extremely helpful answer on working with the
ngx.header["Set-Cookie"]
table.Update
Though you didn’t mention it in your question, looking at your example I saw you not only want to pass the
Set-Cookie
headers to the client but also to send those cookies to thesomeservice
upstream. Again, you are trying to do it a wrong way. Usingproxy_set_header Cookie "$http_cookie; $auth_cookie";
you are appending those cookies including their attributes likePath
,Max-Age
, etc. while theCookie
header should contain only thename=value
pairs. Well, using thelua-nginx-module
this is also posible. You’d need to change the aboveaccess_by_lua_block
to the following one.All the above examples are tested (using OpenResty 1.17.8.2) and confirmed to be workable.
I found no other way but to use the lua module. This should partially answer your question, it does answer the question "How do I get all the Set-Cookie headers".
If you use different directives than
log_by_lua_block
I can’t guarantee you’ll be able to see that particular header, withaccess_by_lua_block
I couldn’t.If you want to pass the stuff to the client, maybe
log_by_lua_block
is a bit late in the workflow, maybe try an earlier one.