39

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.

2
  • 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 8, 2011 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, 2012 at 14:03

3 Answers 3

37

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

This is an example with many domains. Your service might be accessible as service.foo.com and service.bar.com and you need to put foo.com or bar.com in Set-Cookie header accordingly while your backend service always puts backend_host.com in the cookie header:

server {
  server_name ~^service\.(?<domain>.+)$;
  location / {
    proxy_pass http://backend.int/;
    proxy_cookie_domain backend_host.com $domain;
  }
}
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 
    '; 
}
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

1
  • 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, 2012 at 13:59

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .