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 therequest
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.