Proxying HTTP Requests in Node

Apple officially prevents http requests in iOS apps since January 2017 through the App Transport Security (ATS) protocol. Although ATS has been around for a little while now, it was still possible to set domain exceptions in your app and allow http requests to these domains only. However, as of January 2017, this is no longer feasible and all http requests are blocked. We can all agree that this is a good cause and improving the security of our apps should be a top priority to all of us, but it does cause some concern in cases where loading http resources is necessary.

For instance, in a previous project, our iOS app had to load pictures from real estate websites that did not support https. Suddenly - after the January deadline - the app was displaying broken image links for all these resources that were previously loaded through http. As I had no control over these 3rd party services that were serving their website - and pictures - over http instead of https, I had to find another way around this.

Proxying Requests

The solution that I implemented involved proxying these http requests through our API, which obviously supported https. That meant that all http requests for static resources would instead go through our Node.js server over https, which would fetch the resource through http, and then return the result to the iOS app. Here's what the flow looks like when loading an http image, for instance, http://example.com/images/house.jpg:

  • Because this is an http url, the iOS app makes a call to the backend instead of fetching the image directly. The url will be: https://api.backend.com/api/v1/proxy?link=http://example.com/images/house.jpg.
  • The backend server will extract the requested url from the query string (link).
  • The backend will make the http request to that link and will pipe the result directly back to the client.
  • The iOS app receives the image as a response to its original https request, which respects the ATS protocol and is therefore not blocked by Apple.

In Node and Express, the code looks as follow:

  • Route for the backend endpoint:

app.get('/api/v1/proxy', communicationController.proxyRequests);

This tells Express to call the communicationController.proxyRequests method when the route /api/v1/proxy is hit.

  • The proxyRequests method, which is very simple and uses the request module:
     var proxiedUrl = req.query.link || req.url;
     request(proxiedUrl).pipe(res);
}

This retrieves the requested url from the query string (req.query.link), and uses the original url only if that doesn't exist.

That's all there is to proxying http requests! Surprisingly little code to get it done, right?

Conclusion

Although I highly suggest that you use https everywhere, I hope this helps you get around a nasty use case where you must absolutely use http, like it helped me.

Let me know what you think and if you found other ways around this.

Wissam Abirached

Wissam Abirached