Working with quota on mobile browsers

A research report on browser storage

HTML5 Rocks


One of the biggest evolutions happening around HTML5 today is the availability of persistent storage on browsers. Before HTML5, we had no choice other than using cookies to store some kind of data on client side. With HTML5, you have several choices for storing your data, depending on what you want to store.

The most broadly used technology is called WebStorage, an API for persistent key-value data storage which most major browsers have supported for a while. WebStorage has persistent storage called LocalStorage, and temporary storage called SessionStorage which will be cleared after a session.

WebSQL Database is a relational database solution for browsers. Even though it is essentially deprecated and the W3C no longer maintains the specification, meaning browsers may or may not continue to support it, people still use it because it is the only common answer for structured data storage on mobile browsers.

Indexed Database is an object store. Since it's considered to be the final solution for storing structured data on browsers, its support and usage is gradually expanding.

FileSystem API is a file system solution for the web. Developers can store large objects in a sandboxed part of the user's file system and directly link to them via URL. Although Chrome and Opera are the only browsers that currently implement the feature, its standardization is ongoing.

Application Cache is a powerful cache mechanism targeting single page applications on browsers. Although there were number of complaints about this spec and an alternative spec called ServiceWorker is being proposed, there is no real offline webapp solutions other than this as of January 2014.

As web apps get richer and richer, more and more developers are using browser-side storage. However, there hasn't been a major study comparing the limitations of the various storage mechanisms. It is time consuming for developers to research each method and browser. So in this article, I'm going to show you the results of my research on the upper limit of each browser's storage implementation, as well as how you can inspect them.


To do this research, I have built a tool called Browser Storage Abuser to minimize the manual work. You can store as much data as possible to see the limit of each storage on your browser.

Following is a quick summary of results from this research using the tool.

Note: Opera is not listed here because it uses Blink, the same browser engine that Chrome uses, and so my research results using Opera were very similar to my results with Chrome.


Chrome Android Browser Firefox Safari Safari iOS WebView iOS WebView
40 4.3 34 6, 7 8 6, 7 8
Application Cache up to quota Unlimited? 5MB, Unlimited 300MB? 300MB? 100MB? 100MB?
FileSystem up to quota
IndexedDB up to quota 5MB, Unlimited up to quota? up to quota?
WebSQL up to quota 200MB~ 5MB, 10MB, 25MB, 50MB 5MB, 10MB, 25MB, 50MB 50MB 50MB
LocalStorage 10MB 2MB 10MB 5MB 5MB 5MB 5MB
SessionStorage 10MB Unlimited 5MB Unlimited 5MB Unlimited


Chrome Firefox Safari Safari IE IE
40 34 6, 7 8 9 10, 11
Application Cache up to quota 500MB, Unlimited Unlimited? Unlimited? 100MB?
FileSystem up to quota
IndexedDB up to quota 50MB, Unlimited up to quota? 10MB, 250MB (~999MB)
WebSQL up to quota 5MB, 10MB, 50MB, 100MB, 500MB, 600MB, 700MB... 5MB, 10MB, 50MB, 100MB, 500MB, 600MB, 700MB...
LocalStorage 10MB 10MB 5MB 5MB 10MB 10MB
SessionStorage 10MB Unlimited Unlimited 10MB 10MB

Details: Working with real world browsers

OK, now you have a summary of storage limitations. Let's dive into the details of each browser's implementation and see how you can inspect and debug each storage.

Chrome / Chrome for Android

AppCache FileSystem IndexedDB WebSQL LocalStorage SessionStorage
Chrome 40 up to quota up to quota up to quota up to quota 10MB 10MB
Chrome for Android 40 up to quota up to quota up to quota up to quota 10MB 10MB

Chrome can use all of the types of storage listed here including Application Cache, FileSystem API, IndexedDB, WebSQL, LocalStorage and SessionStorage. All of the storage mechanisms except LocalStorage and SessionStorage, which use asynchronous input and output, are under managed by Quota Management API; their limitation varies depending on the amount of storage the device has left.

Current quota rule is as follows:

  • On Temporary Storage (Default), you can use up to 10% of remaining disk space per domain. If you exceed quota, the browser starts clearing out your storage, starting with the oldest domain.
  • On Persistent Storage, you can request arbitrary size if the user permits. Going over quota will cause an error. Keep in mind that on Chrome's (and Opera's) implementation, this only applies to FileSystem API as of version 33. All other storage mechanisms will still use Temporary Storage as its storage.

Please refer to this page for further information regarding Quota Management API (as of January 2014). The Quota Management API specification is under active discussion and subject to change in the future.

How to inspect

You can inspect details of Application Cache, IndexedDB, WebSQL, LocalStorage and SessionStorage if you open DevTools' Resources panel.
For inspecting FileSystem API, you need to

  1. Turn on "Enable Developer Tools experiments" flag in about:flags page.
  2. Restart Chrome and open DevTools.
  3. Select "Experiments" in the Settings and check the box next to "FileSystem inspection".
  4. Reopen DevTools.

You should now see "FileSystem" in the Resources panel.

For clearing and initializing storage content including cookies, follow instructions below.

  1. Open chrome://settings/cookies
  2. Select the domain you want to clear
  3. Click on the cookie or the storage resource you wish to delete, then click "Remove"


  1. You can also delete individual items on DevTools Resources panel


  1. Open "Settings"
  2. Open "Content settings"
  3. Open "Website settings"
  4. Tap the domain
  5. Tap on "Clear stored data"

On Chrome for Android, you can inspect storage types by following the same procedure as desktop version described above. Refer to "Remote debugging" article to learn how to debug Chrome for Android.

Firefox / Firefox for Android

AppCache FileSystem IndexedDB WebSQL LocalStorage SessionStorage
Firefox 34 500MB, Unlimited 50MB, Unlimited 10MB
Firefox for Android 34 5MB, Unlimited 5MB, Unlimited 10MB

Firefox can use IndexedDB, LocalStorage and SessionStorage. LocalStorage and SessionStorage can use up to 10MB of storage but the number is actually the sum of both.
For IndexedDB, you can use up to 50MB on desktop, 5MB on mobile for free. However, the user can allow the limit to be removed by granting permission.

For Application Cache, you can use up to 500MB on desktop, 5MB on mobile for free. Same for user permission. A user can also change the warning quota by changing about:config's dom.indexedDB.warningQuota for IndexedDB, browser.cache.offline.capacity for Application Cache.

Note: Firefox actually stores the IndexedDB data in sqlite as a compressed BLOB, so the size of original object won't be reflected).

How to inspect

If you are running Firefox later than version 34, you can inspect details of IndexedDB, LocalStorage and SessionStorage if you open DevTools' Storage panel. 
In order to use it, you'll first need to check a box next to "Storage" under "Default Firefox Developer Tools" section in the DevTools' Settings. You can inspect actual contents stored in the storage.

If you want to manipulate those data, you need to use sqlite command against the raw database. The sqlite files are located under idb folder under profile directory of respective OSes.

Windows: %AppData%\Mozilla\Firefox\Profiles\xxxxxxxx.default\storage\persistent
Mac: ~/Library/Application Support/Firefox/Profiles/xxxxxxxx.default/storage/persistent

Add-on called SQLite Manager might be a good choice for this purpose using GUI.

If you want to remove permissions per domain, click on the icon left to the URL on the page. Then choose "More Information...".

You can then change permission status of the page's storage from "Maintain Offline Storage" under "Permissions" tab. You can clear the stored data for this page as well.

For Android version, you can inspect storage using similar technique as desktop version once you get the remote debugging working. To learn more about debugging mobile Firefox, see this document. Note that manipulation on raw database is still not possible.

Android Browser

AppCache FileSystem IndexedDB WebSQL LocalStorage SessionStorage
Android Browser Unlimited? 200MB~? 2MB Unlimited

Android Browser can use Application Cache, WebSQL, LocalStorage and SessionStorage.
Compared to the other browsers, Android Browser has rather small 2MB for LocalStorage. SessionStorage is unlimited since it stores data on memory instead of disks.
It is tough to examine WebSQL on Android Browser. Storing around 500KB data in one transaction returns an error, but dividing it into multiple transactions seemed to work ok. I could store up to around 200MB without permission, before it crashed.
Though unconfirmed, Application Cache is said to have no limit on storing data.

How to inspect

You can inspect stored data on WebSQL, LocalStorage and SessionStorage by using an open source tool called weinre, which can interact with the browser remotely using the DevTools protocol.

You can delete those data individually on weinre. If you don't have weinre installed, you need to follow the instructions below to delete them. Note that you can't delete per domain.

Open system's "Settings" > "Apps" > "Browser" and tap "Clear data"

Safari / Safari for iOS

AppCache FileSystem IndexedDB WebSQL LocalStorage SessionStorage
Safari 6, 7 Unlimited? 5MB, 10MB, 50MB, 100MB, 500MB, 600MB, 700MB... 5MB Unlimited
Safari 8 Unlimited? up to quota? 5MB, 10MB, 50MB, 100MB, 500MB, 600MB, 700MB... 5MB Unlimited
Mobile Safari 6, 7 300MB? 5MB, 10MB, 25MB, 50MB 5MB 5MB
Mobile Safari 8 300MB? up to quota? 5MB, 10MB, 25MB, 50MB 5MB Unlimited
iOS WebView (Safari 6, 7) 100MB? 50MB 5MB 5MB
iOS WebView (Safari 8) 100MB? up to quota? 50MB 5MB Unlimited

Safari can use Application Cache, WebSQL, LocalStorage and SessionStorage.
For desktop, you can use up to 5MB for LocalStorage. Similar to Android Browser, SessionStorage is unlimited since it stores data on memory instead of disks.
WebSQL's quota policy is a bit unique here. Safari asks a user for permissions in multiple size points as described above. But if openDatabase is called with more than 5MB of size, Safari will ask the user for a permission of the closest upper limit on database creation. If granted, it won't ask the user another permission until actual data size is about to exceed that number.
Though unconfirmed, Application Cache on Safari is said to have no limit on storing data.

For mobile, the rules are similar but the upper limits are different.
The SessionStorage's limit is 5MB as it is on LocalStorage. On WebSQL, it asks a user for permissions but you can't store data any more than 50MB.
I've seen an information that Application Cache's upper limit is 10MB, however, I could successfully store data up to 300MB at least.

Note: iOS 7 Safari has a bug that throws an exception when requesting more than 5MB on database creation. This is not fixed as of iOS 7.0.4. Work around is to request less than 5MB and let Safari ask for permissions. This is annoying for users, but there's no way around.
Update: This problem seems to have been solved at iOS 7.1. See details here.

On iOS WebView, WebSQL's upper limit is also 50MB but it won't ask for permission and generates an error.

How to inspect

On desktop, you can use Web Inspector to inspect LocalStorage, SessionStorage and WebSQL.

On mobile, you can inspect LocalStorage and SessionStorage. To learn more about debugging mobile Safari, see this document.

Internet Explorer

AppCache FileSystem IndexedDB WebSQL LocalStorage SessionStorage
IE 9 10MB 10MB
IE 10, 11 100MB? 10MB, 250MB (~999MB) 10MB 10MB

Internet Explorer supports LocalStorage and SessionStorage after version 9. Version 10 started to support Application Cache and IndexedDB as well.
On LocalStorage and SessionStorage, users can store up to 10MB each.
On IndexedDB, the browser asks users for permission at 10MB, then allows up to 250MB by default. It can store up to 999MB without permission but requires the user to change their settings to allow it; a developer cannot change this setting.
On Application Cache, I got an error when storing around 100MB but it's difficult to tell that it was caused by going over quota.

How to inspect

Unfortunately, there's no good way to inspect any of supported storage on Internet Explorer yet.
You can still delete stored data per domain by

  1. Open "Internet Options"
  2. Click on "Settings" under "Browsing history" in "General" tab.

How to handle going over quota?

What should you do when your app is over quota? Of course, it's up to app's design, but it's important to understand how APIs respond. I would like to share some sample code here since there's not much existing examples on the internet.

Application Cache

The most tough storage to debug on is Application Cache. Some browsers kindly tell a reason of the error in console when something happens, but since there's no DOMError object associated with the event handler, there's no way to programmatically know what is going on.

var appCache = window.applicationCache;
appCache.addEventListener('error', function(e) {
  // It's difficult to tell cause of this error since there's no information associated with the callback argument.

FileSystem API

There are multiple cases where FileSystem generates quota exceeded error.

When quota exceeds on FileEntry or DirectoryEntry's getFile(), copyTo() or moveTo() functions, an error calls the second function argument with FileError as an argument.

When quota exceeds on FileWriter's write() function, an error calls onerror() function with ProgressEvent as an argument. FileError (planned to be replaced with DOMError) is attached as

To determine if the error is caused by over quota, check if the name property of the FileError object is "QuotaExceededError" or not.

Note that FileSystem will throw a DOMException if you use sync version of the API (FileSystemSync).

filesystem.root.getDirectory(dirName, {create:true, exclusive:false},
function getDirectorySuccess(dirEntry) {
  dirEntry.getFile(fileName, {create:true, exclusive:false},
  function getFileSuccess(fileEntry) {
    fileEntry.createWriter(function(writer) {
      writer.onwriteend = function() {
      writer.onerror = function(progressEvent) {
        var fileError =;
        if ( === 'QuotaExceededError') {
          // Fallback code comes here.
  // argument will be DOMError in the future
  function getFileError(fileError) {
    if ( == 'QuotaExceededError') {
      // Fallback code comes here.
// argument will be DOMError in the future
function getDirectoryError(fileError) {
  if ( == 'QuotaExceededError') {
    // Fallback code comes here.

IndexedDB API

When quota exceeds on IndexedDB API, the error calls the transaction's onabort() function with Event as an argument. To determine if the error is caused by a quota exceed, check if name property of DOMError object is "QuotaExceededError" or not.

When a browser requests user a permission for extending storage size, all browsers call this function only when the user doesn't allow it, otherwise, continue the transaction.

var transaction = idb.transaction(['entries'], 'readwrite');
transaction.oncomplete = function(e) {
transaction.onerror = function(e) {
transaction.onabort = function(event) {
  var error =; // DOMError
  if ( == 'QuotaExceededError') {
    // Fallback code comes here
var req = transaction.objectStore('entries').put(data);
req.onerror = function(e) {
  // Quota error won't call this function

WebSQL Database

WebSQL is a bit tricky. When quota exceeds on WebSQL, the error calls the transaction's second function argument with SQLError as an argument. To determine if the error is caused by a quota exceed, check if code property of SQLError object matches SQLError.QUOTA_ERR or not.

Unfortunately, Safari calls this function regardless of if the user granted extending storage size permission or not. Also, there's no way for a developer to know if the user has actually given a permission.

db.transaction(function onTransaction(t) {
  t.executeSql('INSERT INTO entries (name, size, date, payload) VALUES(?, ?, ?, ?)', data,
  function onExecuteSqlCallback(t, results) {
  function onExecuteSqlError(t, e) {
    // Returning true to rollback, false to continue the transaction;
    return false;
function onTransactionError(sqlError) {
  // Safari calls this function regardless if user permits more quota or not.
  // And there's no way for a developer to know user's reaction.
  if (sqlError.code === sqlError.QUOTA_ERR) {
    // Start over the transaction again to check if user permitted or not.
function onTransactionSuccess() {


When quota exceeds on LocalStorage or SessionStorage, catch exception using try catch. To determine if the error is caused by over quota, check if the name property of DOMException is either "QuotaExceededError" or "NS_ERROR_DOM_QUOTA_REACHED" (on Firefox).

try {
  localStorage.setItem(, JSON.stringify(data));
} catch(domException) {
  if ( === 'QuotaExceededError' || === 'NS_ERROR_DOM_QUOTA_REACHED') {
    // Fallback code comes here.


As you can see above, each browser has unique characteristics when it comes to storage, quota, and limits. It is important to make sure your app won't fail on exceeding quota and gracefully fall back to a meaningful result. Also, check out the ServiceWorker and the new Quota Management API which specifications are under discussion. They will drastically change the way you will interact with the limitations this article described when implemented.

Lastly, please feel free to send us Pull Requests if you find any correction or updates in this article.