diff --git a/src/restic/backend/s3/s3.go b/src/restic/backend/s3/s3.go
index f16b3c594..301598d04 100644
--- a/src/restic/backend/s3/s3.go
+++ b/src/restic/backend/s3/s3.go
@@ -1,7 +1,7 @@
 package s3
 
 import (
-	"bytes"
+	"fmt"
 	"io"
 	"path"
 	"restic"
@@ -139,96 +139,26 @@ func (be *s3) Load(h restic.Handle, length int, offset int64) (io.ReadCloser, er
 		return nil, errors.Errorf("invalid length %d", length)
 	}
 
-	var obj *minio.Object
-	var size int64
-
 	objName := be.Filename(h)
 
 	// get token for connection
 	<-be.connChan
 
-	obj, err := be.client.GetObject(be.bucketname, objName)
-	if err != nil {
-		debug.Log("  err %v", err)
-
-		// return token
-		be.connChan <- struct{}{}
-
-		return nil, errors.Wrap(err, "client.GetObject")
+	byteRange := fmt.Sprintf("bytes=%d-", offset)
+	if length > 0 {
+		byteRange = fmt.Sprintf("bytes=%d-%d", offset, offset+int64(length)-1)
 	}
+	headers := minio.NewGetReqHeaders()
+	headers.Add("Range", byteRange)
+	debug.Log("Load(%v) send range %v", h, byteRange)
 
-	// if we're going to read the whole object, just pass it on.
-	if length == 0 {
-		debug.Log("Load %v: pass on object", h)
+	coreClient := minio.Core{be.client}
+	rd, _, err := coreClient.GetObject(be.bucketname, objName, headers)
 
-		_, err = obj.Seek(offset, 0)
-		if err != nil {
-			_ = obj.Close()
+	// return token
+	be.connChan <- struct{}{}
 
-			// return token
-			be.connChan <- struct{}{}
-
-			return nil, errors.Wrap(err, "obj.Seek")
-		}
-
-		rd := wrapReader{
-			ReadCloser: obj,
-			f: func() {
-				debug.Log("Close()")
-				// return token
-				be.connChan <- struct{}{}
-			},
-		}
-		return rd, nil
-	}
-
-	defer func() {
-		// return token
-		be.connChan <- struct{}{}
-	}()
-
-	// otherwise use a buffer with ReadAt
-	be.cacheMutex.RLock()
-	size, cacheHit := be.cacheObjSize[objName]
-	be.cacheMutex.RUnlock()
-
-	if !cacheHit {
-		info, err := obj.Stat()
-		if err != nil {
-			_ = obj.Close()
-			return nil, errors.Wrap(err, "obj.Stat")
-		}
-		size = info.Size
-		be.cacheMutex.Lock()
-		be.cacheObjSize[objName] = size
-		be.cacheMutex.Unlock()
-	}
-
-	if offset > size {
-		_ = obj.Close()
-		return nil, errors.New("offset larger than file size")
-	}
-
-	l := int64(length)
-	if offset+l > size {
-		l = size - offset
-	}
-
-	buf := make([]byte, l)
-	n, err := obj.ReadAt(buf, offset)
-	debug.Log("Load %v: use buffer with ReadAt: %v, %v", h, n, err)
-	if err == io.EOF {
-		debug.Log("Load %v: shorten buffer %v -> %v", h, len(buf), n)
-		buf = buf[:n]
-		err = nil
-	}
-
-	if err != nil {
-		_ = obj.Close()
-		return nil, errors.Wrap(err, "obj.ReadAt")
-	}
-
-	return backend.Closer{Reader: bytes.NewReader(buf)}, nil
+	return rd, err
 }
 
 // Stat returns information about a blob.