Skip to content

Chunked transfers lead to 0 byte files #7995

Description

@ylangisc

We hit the annoying 0 byte files issue when using a PUT operation with a chunked transfer encoding to modify an existing 0 byte file. After successfully executing the PUT the server replies with a 204 but the file is still empty. Not sure if this is a NC or sabre/dav issue.

curl -v -X PUT --header "Transfer-Encoding: chunked" -d @report.csv "http://ubuntu.local/remote.php/webdav/ylatst.txt" -u admin:admin
*   Trying 192.168.48.135...
* TCP_NODELAY set
* Connected to ubuntu.local (192.168.48.135) port 80 (#0)
* Server auth using Basic with user 'admin'
> PUT /remote.php/webdav/ylatst.txt HTTP/1.1
> Host: ubuntu.local
> Authorization: Basic YWRtaW46YWRtaW4=
> User-Agent: curl/7.54.0
> Accept: */*
> Transfer-Encoding: chunked
> Content-Type: application/x-www-form-urlencoded
> Expect: 100-continue
> 
< HTTP/1.1 100 Continue
< HTTP/1.1 204 No Content
< Server: nginx/1.13.8
< Date: Mon, 22 Jan 2018 16:41:33 GMT
< Content-Type: text/html; charset=UTF-8
< Content-Length: 0
< Connection: keep-alive
< Expires: Thu, 19 Nov 1981 08:52:00 GMT
< Cache-Control: no-store, no-cache, must-revalidate
< Pragma: no-cache
< Set-Cookie: oc_sessionPassphrase=xlmQsVcgE%2FcbE8%2FmB8bvsVG3fGrkDGB%2Fg5wHy5sJqrIjfeVotxGf4US%2BnLdGj6xNmzPjH3NzYgm09Qy%2FnkqKQA59LI9qQEmY0mkZZs17wwCvcubybbqiCS83FHYGdvhe; path=/; HttpOnly
< X-Frame-Options: SAMEORIGIN
< X-XSS-Protection: 1; mode=block
< X-Content-Type-Options: nosniff
< X-Robots-Tag: none
< X-Download-Options: noopen
< X-Permitted-Cross-Domain-Policies: none
< Set-Cookie: nc_sameSiteCookielax=true; path=/; httponly;expires=Fri, 31-Dec-2100 23:59:59 GMT; SameSite=lax
< Set-Cookie: nc_sameSiteCookiestrict=true; path=/; httponly;expires=Fri, 31-Dec-2100 23:59:59 GMT; SameSite=strict
< Content-Security-Policy: default-src 'none';
< Set-Cookie: oc4nuj3qa5xi=upmvsv2vtom8dg52tn72r3g4a0; path=/; HttpOnly
< Set-Cookie: cookie_test=test; expires=Mon, 22-Jan-2018 17:41:33 GMT; Max-Age=3600
< OC-FileId: 00000180oc4nuj3qa5xi
< ETag: "46ca66c845120b954836fb1dc6fb4af9"
< OC-ETag: "46ca66c845120b954836fb1dc6fb4af9"
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< X-Robots-Tag: none
< X-Download-Options: noopen
< X-Permitted-Cross-Domain-Policies: none

I'm using nginx 1.13.8 as a webserver (nginx 1.10.3 doesn't work either). My nginx configuration is quite default. The relevant part:

location ~ ^/(?:index|remote|public|cron|core/ajax/update|status|ocs/v[12]|updater/.+|ocs-provider/.+)\.php(?:$|/) {
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        fastcgi_pass      unix:/var/run/php/php7.0-fpm.sock;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
        fastcgi_intercept_errors on;
        fastcgi_request_buffering off;
    }

Some observations:

  • changing fastcgi_request_buffering off to fastcgi_request_buffering on solves the issue but this is not an option for large streamed files.
  • it does happen for files larger than 7kb in my local environment. Smaller files work fine. For servers not being in my local network the files have to be even larger to show the issue. It's much more difficult to replicate the issue in such an environment. Due to bandwidth limitation and latency the server probably has enough time to process the data and a buffer overflow or similar is less likely to happen.

Background: I'm a developer of Mountain Duck which uses the create/update file pattern described here http://sabre.io/dav/0bytes/. According this document nginx in a newer version should work fine. We have several users hitting this issue though.

Steps to reproduce

  1. Upload a 0 byte file
  2. Try to update the file with chunked transfer encoding (see above for a curl example)

Expected behaviour

Files has updated content and correct size

Actual behaviour

Remote file is not touched at all and you still see 0 bytes

Server configuration

Operating system:
Ubuntu 16

Web server:
nginx

Database:
MariaDB

PHP version:
7.0.22

Nextcloud version: (see Nextcloud admin page)
12.0.4

Updated from an older Nextcloud/ownCloud or fresh install:
Fresh

Where did you install Nextcloud from:
Official download

Activity

  1. ylangisc commented on Jan 23, 2018

    @ylangisc
    Author

    After some more testing I've noticed that it's even worse. It does not matter if you create a 0 byte file prior updating it. Basically all PUT operations with a chunked transfer encoding lead to 0 byte files in low latency and high bandwidth environments for files that are greater than a certain size (see above). In my local environment it's easy to replicate with a simple curl command. E.g.

    curl -v -X PUT --header "Transfer-Encoding: chunked" -d @testfile.bin "http://ubuntu.local/remote.php/webdav/testfile.bin" -u admin:admin
    
  2. nextcloud-bot commented on Jul 5, 2018

    @nextcloud-bot
  3. dkocher commented on Jul 5, 2018

    @dkocher
  4. Greek64 commented on Oct 20, 2018

    @Greek64

    I too am having this issue with webdav.
    After disabling encryption and sniffing the traffic going to and from the nextcloud webserver (nginx 1.14.0 in my case), I too came to the conclusion, that after a PUT command with chunked transfer encoding, nextcloud responses with 204 no content and creates only 0 byte files.

    I noticed this behavior when trying to export my Kodi library via webdav to nextcloud.

    I can also provide a sanitized HTTP communication if needed, but I guess that will not help much.

  5. jospoortvliet commented on Jan 3, 2019

    @jospoortvliet
    Member

    I just tested on nextcloud 15 with a 700 kb image. 256 kb of the image made it to the server. a 7.5 mb ODP file was also cut in half - 3.3 mb on the server.

    curl -v -X PUT --header "Transfer-Encoding: chunked" -d @test.odp "http://127.0.0.1/nextcloud-15/remote.php/webdav/test.odp" -u jos:jos
    *   Trying 127.0.0.1...
    * TCP_NODELAY set
    * Connected to 127.0.0.1 (127.0.0.1) port 80 (#0)
    * Server auth using Basic with user 'jos'
    > PUT /nextcloud-15/remote.php/webdav/test.odp HTTP/1.1
    > Host: 127.0.0.1
    > Authorization: Basic am9zOmpvcw==
    > User-Agent: curl/7.62.0
    > Accept: */*
    > Transfer-Encoding: chunked
    > Content-Type: application/x-www-form-urlencoded
    > Expect: 100-continue
    > 
    < HTTP/1.1 100 Continue
    * Signaling end of chunked upload via terminating chunk.
    < HTTP/1.1 201 Created
    < Date: Thu, 03 Jan 2019 11:22:50 GMT
    < Server: Apache
    < X-Powered-By: PHP/7.3.0
    < Set-Cookie: oczswzpgpvdq=9qbdh7bhvlq6s9um76vqfs7dlg; path=/nextcloud-15; HttpOnly
    < Expires: Thu, 19 Nov 1981 08:52:00 GMT
    < Cache-Control: no-store, no-cache, must-revalidate
    < Pragma: no-cache
    < Set-Cookie: oc_sessionPassphrase=I1gqnptmzWjmuBJhZqrfPOeNr6LIhTAn6OWTsFxQhflPr2EuxY2LtSmdoIMc0bz4ehfVuMBRmLO%2FZBBQG6FEDfgc0Q7qupNRWiHZPR%2BxESiNJg5Vh1%2Fajn6geMMRJNWv; path=/nextcloud-15; HttpOnly
    < Content-Security-Policy: default-src 'none';
    < X-Frame-Options: SAMEORIGIN
    < X-XSS-Protection: 1; mode=block
    < X-Content-Type-Options: nosniff
    < X-Robots-Tag: none
    < X-Download-Options: noopen
    < X-Permitted-Cross-Domain-Policies: none
    < Referrer-Policy: no-referrer
    < Set-Cookie: nc_sameSiteCookielax=true; path=/nextcloud-15; httponly;expires=Fri, 31-Dec-2100 23:59:59 GMT; SameSite=lax
    < Set-Cookie: nc_sameSiteCookiestrict=true; path=/nextcloud-15; httponly;expires=Fri, 31-Dec-2100 23:59:59 GMT; SameSite=strict
    < Set-Cookie: oczswzpgpvdq=pj5kbp17hlf3btfob60d2meh7q; path=/nextcloud-15; HttpOnly
    < Set-Cookie: cookie_test=test; expires=Thu, 03-Jan-2019 12:22:50 GMT; Max-Age=3600
    < OC-FileId: 00001151oczswzpgpvdq
    < Content-Length: 0
    < ETag: "7ef58d6f933a4e3387fdf1418f188772"
    < OC-ETag: "7ef58d6f933a4e3387fdf1418f188772"
    < Content-Type: text/html; charset=UTF-8
    < 
    * Connection #0 to host 127.0.0.1 left intact
    
  6. added this to the Nextcloud 16 milestone on Jan 3, 2019
  7. added
    0. Needs triagePending check for reproducibility or if it fits our roadmap
    on Jan 3, 2019
  8. 84 remaining items

  9. susnux commented on Sep 4, 2025

    @susnux
    Contributor

    I trust the nextcloud-aio-apache image is already properly configured to avoid this type of issue, yes?

    It uses Apache and for Apache we already fixed this in the .htaccess a long time ago.

  10. bohwaz commented on Sep 11, 2025

    @bohwaz

    Commenting on the integrity verification: HTTP does provide this kind of thing with the Content-MD5 header (not implemented by NextCloud AFAIK), and NextCloud also has their own non-standard header called OC-Checksum.

    But I'm not sure that this is used by the NextCloud client for multi-part uploads?

  11. susnux commented on Sep 11, 2025

    @susnux
    Contributor

    @bohwaz no the Content-MD5 header is not part of HTTP anymore, see Appendix B:
    https://www.rfc-editor.org/rfc/rfc7231

    So yes Nextcloud has its own header for downloads (OC-Checksum).

  12. vpecinka commented on Jan 12, 2026

    @vpecinka
    Contributor

    I ran into similar problem when uploading bigger file via browser. When the file was like 100MB, it was reported as 0 B, but in the object storage (I have Swift) was correct.

    This happens when using Safari on my Mac. The first upload of a bigger file (=multipart upload) leads to 0 size in metadata, correct size in the storage. The 2nd upload (rewrite) results in correct metadata size.

    When using Chrome, it was always ok - even the 1st upload was ok.

    I didn't found the exact reason why 2 different browsers lead to different results but found, that in the code is a place, where based on size === null the real size is calculated. But for Safari a zero arrives ("0"), even fstat says "bigger than zero". I added the following "safety patch" that solves the issue:

    Maybe this helps:

    --- lib/private/Files/ObjectStore/ObjectStoreStorage.php.orig	2026-01-12 15:53:11
    +++ lib/private/Files/ObjectStore/ObjectStoreStorage.php	2026-01-12 15:55:09
    @@ -457,6 +457,19 @@
     	}
     
     	public function writeStream(string $path, $stream, ?int $size = null): int {
    +		$stats = is_resource($stream) ? @fstat($stream) : null;
    +		$fstatSize = is_array($stats) ? ($stats['size'] ?? null) : null;
    +
    +		if ($size === 0 && is_int($fstatSize) && $fstatSize > 0) {
    +			$this->logger->error('ObjectStoreStorage::writeStream forcing size=null (size_arg=0 but fstat_size>0)', [
    +				'app' => 'objectstore',
    +				'path' => $path,
    +				'size_arg' => $size,
    +				'fstat_size' => $fstatSize,
    +			]);	 
    +			$size = null;
    +		}
    +
     		if ($size === null) {
     			$stats = fstat($stream);
     			if (is_array($stats) && isset($stats['size'])) {
    
  13. szaimen commented on Jan 29, 2026

    @szaimen
    Contributor

    Hi, we have some code since a while to prevent this bug from happening now in our .htaccess file:

    server/.htaccess

    Lines 178 to 189 in 7f71b46

    # Clients like xDavv5 on Android, or Cyberduck, use chunked requests.
    # When FastCGI or FPM is used with apache, requests arrive to Nextcloud without any content.
    # This leads to the creation of empty files.
    # The following directive will force the problematic requests to be buffered before being forwarded to Nextcloud.
    # This way, the "Transfer-Encoding" header is removed, the "Content-Length" header is set, and the request content is proxied to Nextcloud.
    # Here are more information about the issue:
    # - https://docs.cyberduck.io/mountainduck/issues/fastcgi/
    # - https://docs.nextcloud.com/server/latest/admin_manual/issues/general_troubleshooting.html#troubleshooting-webdav
    <IfModule mod_setenvif.c>
    SetEnvIfNoCase Transfer-Encoding "chunked" proxy-sendcl=1
    </IfModule>

    Can you please retest if this is still happening with the latest Nextcloud releases? Thanks in advance!

  14. added
    0. Needs triagePending check for reproducibility or if it fits our roadmap
    and removed
    1. to developAccepted and waiting to be taken care of
    on Jan 29, 2026
  15. susnux commented on Jan 29, 2026

    @susnux
    Contributor

    @szaimen yes it still happens in e.g. Nginx.
    This is a upstream PHP problem as they refuse to forward data streams in PHP FPM when no content length is available.
    Other FastCGI solutions do this and Nginx as well as Apache also support this - just PHP FPM is broken.

  16. mosi-kha commented on Aug 23, 2026

    @mosi-kha
  17. susnux commented on Aug 23, 2026

    @susnux
  18. mosi-kha commented on Aug 23, 2026

    @mosi-kha
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions