#8241·capacitor

[Bug]: capacitor/http Upload progress not supported

Author: gasciCreated Nov 14, 2025Updated Sep 10, 2026

Capacitor Version

I would like to address an issue that was automatically closed. I am having the same problem.

https://github.com/ionic-team/capacitor/issues/6658

Currently, the Capacitor HTTP plugin does not emit upload progress events, so passing onUploadProgress in requests has no effect. This is not a bug in user code — it is a limitation of the plugin: • iOS: Uses URLSession.shared without a delegate. Upload progress requires implementing urlSession(_:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:). • Android: Uses RequestBody directly without a wrapper. Upload progress requires a custom RequestBody that reports bytes written.

By contrast, the Capacitor File Transfer plugin handles native upload progress correctly on both platforms.

Upload progress is critical for mobile apps that handle large files. Adding native support would allow the Capacitor HTTP plugin to: 1. Support progress bars and UI feedback during uploads. 2. Reach feature parity with web APIs like XHR and Axios. 3. Make Capacitor a complete solution for file uploads without relying on additional plugins.

Request: Please add native upload progress support on iOS and Android, and expose it via notifyListeners("uploadProgress") so developers can track upload progress natively.

Platforms Affected

  • iOS
  • Android
  • Web

Current Behavior

I hooked up the onprogress event in my code to show the progress of downloading a file to the user. But enabling the @capacitor/http plugin the progress doesn't show up anymore. I only see the popup and when the downloading is done, it will show the file. When I disable the plugin, the progress is shown again.

Expected Behavior

The user should see the progress of the download.

Project Reproduction

javascript
makeXMLRequest(method, params, headers, useDatabase=true, cancelFunc, progressFunc)  {
  const req = this.checkParams(params, headers, useDatabase);
  if (req.error !== undefined && req.error !== null) {
    return Promise.reject(req.error);
  }

  return new Promise((resolve, reject) => {
    let fetch = new XMLHttpRequest();

    if (cancelFunc !== undefined && cancelFunc !== null) {
      cancelFunc.cancel = () => {
        fetch.abort(null);
      };
    }
    // open a connection
    fetch.open("POST", req.host);
    // set extra request headers
    for (const key in req.headers) {
      fetch.setRequestHeader(key, req.headers[key]);
    }

    fetch.onload = (oEvent) => {
      if (fetch.status >= 200 && fetch.status < 300) {
        // difference between @capacitor/http and the 'normal' XMLHttpRequest
        // @capacitor/http returns already a JSON
        const result = JSON.parse(fetch.response);
        resolve(result.result);
      } else {
        reject(fetch.statusText);
      }
    };
    // send progress data back
    fetch.onprogress = (oEvent) => {
      if (progressFunc !== undefined && progressFunc !== null) {
        progressFunc(oEvent.loaded, oEvent.total);
      }
    };

    fetch.onerror = (oEvent) => {
      reject(fetch.statusText);
    };
    // abort the connection
    fetch.onabort = (oEvent) => {
      reject(false);
    };
    // send the data
    fetch.send(JSON.stringify(
      {method: method, params: req.params, id: req.uid}
    ));
  });
}