Securing Sensitive API Calls with Nginx
We sometimes wish to add an extra layer of authorization between end users and the API. An nginx reverse proxy is a simple way to do so.
A common web application architecture relies on a client-facing app that interacts with an API server. For example, you may build a React app that interacts with an API. We’ve worked on a number of projects where we interact with a third-party API that we do not control.
In these instances, we sometimes wish to add an extra layer of authorization between end users and the API. This is necessary when the API permits the client to take irreversible action—such as deletion of data—with no way to restrict or grant privileges to specific end users.
There are a few ways to augment the API’s authorization with our own. We typically take one of two approaches. If the API service is one piece of a number of backend services we need to control, we will build a middleware application that adds our own layer of user authentication and authorization. That solution can add a lot of time and complexity when we’re looking to deliver a simple web app. Alternatively, if the API service is the only backend service we use for the application, we can implement a simpler solution. Instead of building middleware to manage privileges, we can configure nginx as a reverse proxy between end users and the API.
The difference between proxies and reverse proxies
A proxy is a service that acts as an intermediary between the client requesting a resource and the service that provides a resource. There are two types of proxies: regular (“forward”) proxies and reverse proxies. A forward proxy typically obscures which client is requesting a resource. In contrast, a reverse proxy will obscure which service is providing a resource.
Typical API interactions
Before we demonstrate how a reverse proxy helps us solve our security concerns, let’s consider a typical API interaction. Imagine we have a React app that needs to retrieve data from a weather API. The interaction diagram for requesting data from the weather API may look something like this:
The React app client requests data from the weather API, which returns data that the client can present to the user.
Reverse proxied API interactions
Continuing with our example, let’s imagine that we are using a paid weather service API. We are charged for every API call we make. For this scenario, let’s pretend we’re okay with our users making as many requests as they like to the API. However, we’d like to prevent third party websites from taking our API authentication token and using it for their own applications. We would not want to get charged for a competing weather site’s API usage.
So let’s introduce a reverse proxy between our client and the weather API. Instead of storing our API authentication token in our React app, we can store the token securely on our reverse proxy server. Our React app will send API requests to the reverse proxy. The reverse proxy, in turn, will add the API authentication token to our request before forwarding the request to the weather API. The weather API responds to the request, returning our weather data to the reverse proxy. The reverse proxy then sends that data back to the client.
By never directly calling the weather API from our React app, we have hidden our API authentication token from our end users. They can’t view the source code in their browser to uncover our API token, because it’s simply not there.
How to authenticate API calls through an nginx reverse proxy
Setting up a reverse proxy may sound daunting at first. However, nginx makes configuring a reverse proxy rather easy.
Let’s imagine that the weather API requires us to send an X-Weather-API-Token
header containing our API authentication token with every API request. We can tell our nginx server to forward requests from /weather-api/
to the actual weather service API, and to add the required API token header to the forwarded request:
location /weather-api/ {
proxy_cache off;
proxy_set_header X-Weather-API-Token "your-super-secret-api-token";
proxy_pass https://weather.example.com/api/current-weather.json;
}
Now when our React app sends a request to the /weather-api/
URL hosted by our nginx reverse proxy, nginx will add the X-Weather-API-Token
to our request and send the request to the API endpoint located at https://weather.example.com/api/current-weather.json
. The API endpoint will return the data we want to the reverse proxy, and then the reverse proxy will send that data back to our React app.
It’s that easy! Of course, this is a simple example. We did not cover how to ensure no third-party sites hit our reverse proxy. However, you have some options: limit requests to certain origins (hostnames or IP addresses) with a Content Security Policy, set up Basic Authentication, or create a custom authentication mechanism for nginx to use.
This approach has helped us add an extra layer of protection for third-party APIs and has helped us limit client access to certain endpoints. Of course, reverse proxies have plenty of other uses beyond securing an endpoint. You can transform data, combine data from multiple resources, load balance requests between multiple servers, cache data, compress data, and even chunk (or “spoon feed”) large requests.