Android - 404 Not Found (from cache)

I encountered a frustrating issue the other day after upgrading to PhoneGap version cli-5.2.0 on Android.  My requests to Fitbit’s API began to fail, resulting in 404 errors. I was incredibly stumped - the exact same code worked fine on iOS.  I ran the code on my iPhone and my Android tablet and pulled up both requests side by side.  All details in the requests were the same: URL, HTTP method, etc.  Everything except the status code - 200 OK for iOS and 404 Not Found (from cache) for Android.

Why a 404, specifically? Was something wrong on Fitbit’s end?  Couldn’t be - it went through on Android.  Finally it hit me - “(from cache)”?  That’s odd!  After some Google searches, I had the answer.

As the Cordova docs explain, the more recent PhoneGap “cli” type versions coupled with Android’s security model require the Cordova whitelist plugin.  I was testing on an Android 4.4 device, so that makes sense.  What the first link doesn’t explain, but this blog post does, is that “Network requests are blocked by default without the plugin.”  Ok then!

After installing the whitelist plugin and rerunning my code, I got an error pop-up with the following message:

“SecurityError: Refused to load the script ‘https://api.fitbit.com/’ because it violates the document’s Content Security Policy.”

The whitelist plugin docs explain that there are 4 types of security policies to be aware of:

  1. Navigation: Controls which URLs the WebView itself can navigate to
  2. Intent: Controls which URLs the app is allowed to ask the system to open.  This doesn’t apply to plugins - only hyperlinks and calls to window.open( )
  3. Network Request: Controls which network requests (images, XHR/HTTP) are allowed to be made via Cordova native hooks.
  4. Content Security Policy: Controls which network requests (images, XHR/HTTP) are allowed to be made via the webview directly.

I load images and make REST API calls via the webview directly, so I needed a CSP.  After much fiddling, consulting links like this one, I got it to work.  Add this to your index.html page to the <head> element:

<meta http-equiv=“Content-Security-Policy” content=“default-src *; style-src 'self’ 'unsafe-inline’; script-src 'self’ 'unsafe-eval’ 'unsafe-inline’; media-src *”>

Yes, the “unsafe” portions are troubling, but apparently they are required for PhoneGap to work properly. 

Additionally, I needed the Network Request portion, which is set in config.xml:

<access origin=“*://*.fitbit.com/*” />

<access origin=“*://*.cloudfront.net/*” />    (Fitbit profile images hosted here)

You’ll be tempted to just use origin=“*” (asterisk to allow all requests) but keep your app secure by explicitly allowing access only from the resources you will connect to!

To explain why I saw the 404 issue: by not specifying the CSP in my HTML file, the whitelist plugin most likely uses a default, very restrictive one that would not allow for external HTTP requests.  When my code tried to make one, it was blocked and instead looked for the requested resource (api.fitbit.com) locally within the app.  Of course, that route/path doesn’t exist, hence the 404 not found error.  This explanation is mostly just pieced together from the documentation I’ve shared here, so if anyone out there knows more, I’d love to know!

Learn how to create mobile apps with JavaScript. Get occasional PhoneGap/Cordova and Ionic tips, tutorials, and more:

comments powered by Disqus