38

I have a simple nginx reverse proxy:

server {
  server_name external.domain.com;
  location / {
    proxy_pass http://backend.int/;
  }
}

The problem is that Set-Cookie response headers contain ;Domain=backend.int, because the backend does not know it is being reverse proxied.

How can I make nginx rewrite the content of the Set-Cookie response headers, replacing ;Domain=backend.int with ;Domain=external.domain.com?

Passing the Host header unchanged is not an option in this case.

Apache httpd has had this feature for a while, see ProxyPassReverseCookieDomain, but I cannot seem to find a way to do the same in nginx.

Tobia
  • 1,143
  • 1
  • 12
  • 19
  • 2
    why is passing the host header not an option? imo the host part of the header is made for such things. if you need to pass which proxy was used, you should supply additional headers. – jojoo Sep 08 '11 at 14:28
  • 2
    Suppose you have a legacy server that does virtual hosting and you want to put Nginx in front of it, to publish some of those services on a new domain. Suppose also you cannot (or don't want to) change the legacy server's configuration. Nginx contains all the tools needed to publish legacy services on new sites, except for the cookie domain issue. – Tobia Aug 10 '12 at 14:03

3 Answers3

33

Starting in 1.1.15, proxy_cookie_domain option was added to address this issue.

http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_cookie_domain

M. Schmidt
  • 183
  • 14
rrehbein
  • 458
  • 4
  • 9
5

The answer from @shamer works fine with multiple Set-Cookie response headers, but it fails if there's just one. As agentzh points out at the end of the referenced thread, if type(cookies) ~= "table" then cookies = {cookies} end is needed to handle that case.

Here's the whole thing:

location / { 
    proxy_pass http://backend.int/;

    header_filter_by_lua '
        local cookies = ngx.header.set_cookie 
        if not cookies then return end
        if type(cookies) ~= "table" then cookies = {cookies} end
        local newcookies = {}
        for i, val in ipairs(cookies) do
            local newval = string.gsub(val, "([dD]omain)=[%w_-\\\\.]+", 
                      "%1=external.domain.com") 
            table.insert(newcookies, newval) 
        end 
        ngx.header.set_cookie = newcookies 
    '; 
}
lhagan
  • 51
  • 1
  • 2
2

This question came up in the nginx mailing list [1]. There's no way to directly do this in nginx. You have to resort to using the ngx_lua module (>=v0.3.1).

The user "agentzh" has an example of what this would look like inlined in the config file:

    server_name external.domain.com; 

    location / { 
        proxy_pass http://backend.int/;

        header_filter_by_lua ' 
            local cookies = ngx.header.set_cookie 
            if not cookies then return end 
            local newcookies = {} 
            for i, val in ipairs(cookies) do 
                local newval = string.gsub(val, "([dD]omain)=[%w_-\\\\.]+", 
                          "%1=external.domain.com") 
                table.insert(newcookies, newval) 
            end 
            ngx.header.set_cookie = newcookies 
        '; 
    } 

[1] http://nginx.2469901.n2.nabble.com/Rewriting-the-domain-part-of-Set-Cookie-in-a-proxy-pass-td6453554.html

shamer
  • 121
  • 5
  • 3
    Thank you for the correct answer, although I've had bad experience with ngx_lua in the past: bad memory leaks. I think Nginx needs some simple header manipulation primitives using its builtin regexp engine, if not a few more custom instructions such as cookie domain rewriting. – Tobia Aug 10 '12 at 13:59