Nginx Redirect Pitfalls

Background

Content delivery networks can improve website performance and absorb burst traffic with cache on their edge servers. Some CDN providers even support accelerating dynamic content delivery by making use of their optimized backbone network to reduce latency.

CDN.png

To make effective use of CDNs, we need to ensure that users are accessing our accelerated CDN domains and not the origin domain that points directly to our servers. It's also important to hide the origin domain from the public to avoid DDoS attacks that could overload our servers.

What's it about Nginx?

Nginx has some implicit behaviors that may send out the origin domain to users in the Location header, so if you happen to use the origin domain in your Nginx configuration, which is quite common, beware of these pitfalls.

Unintended redirects

A common use of Nginx is to dispatch requests for different paths to different server groups. For example, we may forward /api/ requests to our API servers and the rest to our frontend servers that does server-side rendering (SSR).

http {
  upstream example_web {
    server 192.168.0.101;
  }

  upstream example_api {
    server 192.168.0.100;
  }

  server {
    listen 80;
    server_name origin.example.com;

    location / {
        proxy_pass http://example_web;
    }

    location /api/ {
        proxy_pass http://example_api;
    }
  }
}

Assuming the CDN domain is www.example.com and a user opens http://www.example.com/api in the browser, you would have expected the request to be sent to the web server at 192.168.0.101. But what we actually got is a 301 redirect to the origin.

HTTP/1.1 301 Moved Permanently
Server: nginx/1.22.0
Date: Sat, 27 Aug 2022 07:47:47 GMT
Content-Type: text/html
Content-Length: 169
Location: http://origin.example.com/api/

The reason is that Nginx tries to be smart and sends a redirect with the slash appended when you have a prefix-maching location that ends in slash and in which requests are processed by proxy_pass.

The solution is to either explicit specify the behavior of /api, e.g. location = /api, or use a regular expression location instead, which is not affected.

    location ~ ^/api/ {
        proxy_pass http://example_api;
    }

Rewriting redirects

Nginx has enabled proxy_redirect configuration by default, which for example rewrites Location: http://example_web/index in response header to Location: http://origin.example.com/index, again exposing the origin.

To return the CDN domain instead and hide other internal domains from unintended exposure, we could explicitly specify proxy_redirect with a regular expression replacement.

    location / {
        proxy_pass http://example_web;
        proxy_redirect ~^https?://[^/]+(/.+)$ https://www.example.com$1;
    }

This would not rewrite path-style redirects like Location: /index because the regex does not match, but users will be redirected relative to the domain they are visiting, so it's not a problem.

H2
H3
H4
3 columns
2 columns
1 column
Join the conversation now
Logo
Center