I've spent a day trying to debug an NGINX proxy issue for a client. I thought it might be worthy of a blog post.
When building applications in a microservices architecture, it is common for the UI--Angular or React--to be on an entirely different web domain than the services. The browser can't call services on a different domain directly due to cross site scripting issues. A common solution is to set use a server side proxy, and NGINX has one built right in.
I want to set something up so that when my UI loads:
my-ui.com/thirdPartyService
The server will know to forward that to:
my-service.com/thirdPartyService
And my UI code gets the results w/o issue.
Since the call to the remote service is being done by NGINX--a server software--the browser does not block it. Server to server network traffic is generally considered safer and less interceptable than browser to server network traffic.
Take your nginx config file. Inside HTTP section, there should be a server section. Inside the server section you probably have multiple locations set up. Let's set up a new one:
2}
Now, whenever the server sees the "my-ui.com/thirdPartyService" it knows to process it in that location block. Let's flesh it out. First we need to add a resolver:
The resolver tells the server the IP of where the remote URL is. For simplicity here I have it redirecting to my local machine, but your real world app is going to be more complex I bet.
How add some proxy headers into the location block:
2proxy_set_header X-Real-IP $remote_addr;
These just adds header.
X-Forwarded-Proto is the standard header used for connecting to a proxy or load balance.
The X-Real-IP doesn't have a lot of documentation available on the Internet. I think it might be an NGINX specific thing. It is a way to send the proxy the client's IP address instead of the proxy's address. So when calling this new URL on my server, the client will see me--the user sitting at my browser--and not the IP address of the server.
The $remote_addr is an NGINX variable, representing the client's IP address.
Now, I need to add a rewrite rule. I spent quite a bit of time debugging the lack of this last week:
This rule introspects your URL, and pulls out all portions of it after the `thirdPartyService` and saves it in the $1 variable. We'll re-use this value in a second.
For example, this:
my-service.com/some/end/point
And saves it to:
some/end/point
We use that $1 rewrite rule along with proxy_pass:
$1 comes from the rewrite rule. $args is a special variable with all URL arguments pulled out of the URL.
The full location block, for reference:
2 resolver 127.0.0.1 valid-15s;
3 proxy_set_header X-Real-IP $remote_addr;
4 proxy_set_header X-Forwarded-Proto http;
5 rewrite ^/thirdPartyService/(.*) /$1 break;
6 proxy_pass https://my-service.com/$1/$args;
7}
So, when our UI loads:
my-ui.com/thirdPartyService/Something/Else?arg1=foo&arg2=bar
The server will know to forward that to:
my-service.com/Something/Else?arg1=foo&arg2=bar
Stuff like this is often something I set up once or twice and then they just persist w/o issue. As such, I don't have to set them up too often and it usually takes a lot of grinding to remember how I got it to work last time. Hopefully this post will help me remember next time.