When implementing a reverse proxy with Nginx, one common challenge arises when backend servers set cookies with their internal domain. Consider this basic Nginx reverse proxy configuration:
server {
server_name external.domain.com;
location / {
proxy_pass http://backend.int/;
}
}
The backend application might set cookies like this in its HTTP response headers:
Set-Cookie: session_id=abc123; Domain=backend.int; Path=/; Secure; HttpOnly
Modern browsers enforce strict cookie security policies. When they receive a cookie with Domain=backend.int but the request came from external.domain.com, they'll reject the cookie because the domains don't match. This breaks authentication and session management for users.
Nginx provides the proxy_cookie_domain
directive specifically for this scenario. Here's how to implement it:
server {
server_name external.domain.com;
location / {
proxy_pass http://backend.int/;
proxy_cookie_domain backend.int external.domain.com;
# Additional recommended proxy settings
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
For more complex scenarios where you need to handle multiple domains or paths:
# Replace multiple possible backend domains
proxy_cookie_domain ~^(www\.)?backend\.int$ $host;
# Handle path rewriting if needed
proxy_cookie_path / /api/;
After implementing these changes, verify with curl:
curl -I https://external.domain.com/
You should see the rewritten cookie domain in the response headers:
Set-Cookie: session_id=abc123; Domain=external.domain.com; Path=/; Secure; HttpOnly
Cookie rewriting adds minimal overhead, but for high-traffic sites:
- Consider caching strategies
- Monitor proxy performance metrics
- Keep Nginx updated for latest optimizations
If modifying Nginx config isn't possible, consider:
- Configure the backend to be aware of its public domain
- Use application-level cookie handling
- Implement a middleware layer for header transformation
When using Nginx as a reverse proxy, a common challenge arises with Set-Cookie
headers from backend applications. The backend typically sets cookies with its own domain (e.g., backend.int
), unaware that it's being accessed through a different public domain (external.domain.com
).
# Problematic Set-Cookie header example from backend
Set-Cookie: sessionid=abc123; Path=/; Domain=backend.int; Secure; HttpOnly
Nginx provides the ngx_http_sub_module
for response header modification. Here's how to implement it:
server {
server_name external.domain.com;
# Enable response header processing
proxy_set_header Accept-Encoding "";
location / {
proxy_pass http://backend.int/;
# Process Set-Cookie headers
proxy_cookie_domain backend.int external.domain.com;
# Alternative regex-based approach for complex cases
sub_filter_once off;
sub_filter_types *;
sub_filter '; Domain=backend.int' '; Domain=external.domain.com';
sub_filter ';Domain=backend.int' ';Domain=external.domain.com';
}
}
For more complex scenarios with multiple domains or wildcards:
map $upstream_http_set_cookie $new_cookie {
~(.*)(Domain=)(backend\.int|\.backend\.int)(.*) $1$2external.domain.com$4;
default $upstream_http_set_cookie;
}
server {
server_name external.domain.com;
location / {
proxy_pass http://backend.int/;
proxy_hide_header Set-Cookie;
add_header Set-Cookie $new_cookie;
}
}
- The
proxy_set_header Accept-Encoding "";
line is crucial as it disables compression, allowing Nginx to modify responses - For production environments, consider adding
proxy_cookie_path / /;
if you need to rewrite cookie paths - Test thoroughly as cookie modifications can break authentication flows
While these solutions work, they do add overhead:
- The sub_filter approach scans entire responses
- The map-based solution requires additional memory
- For high-traffic sites, consider modifying the backend application instead