Allow web browsers to cache publicly shared files
Nobody has claimed this yet.
- Dominant language
- PHP
- Stars
- 36.9k
- Forks
- 5.2k
- Avg merge
- 2d 3h
- Merged PRs (30d)
- 713
Description
How to use GitHub
- Please use the 👍 reaction to show that you are interested into the same feature.
- Please don't comment if you have no relevant information to add. It's just extra noise for everyone subscribed to this issue.
- Subscribe to receive notifications on status change and new comments.
Is your feature request related to a problem? Please describe.
When sharing a file or folder via a public link, Nextcloud serves the download in a way that does not enable functioning caching. It sets cache-control: must-revalidate, post-check=0, pre-check=0, which asks browsers to revalidate each request (which could be okay), but it does not set last-modified or etag, and it does not respect if-modified-since or if-none-match requests.
For an image file preview of a public share, caching works: cache-control includes a max-age, the last-modified response header gives the date the preview was first requested (I assume), and conditional requests with a recent enough if-modified-since return status 304.
(On a side note, "pragma: no-cache" in the response is deprecated, and "post-check" and "pre-check" were never standardized in the "cache-control" header.)
Describe the solution you'd like
The PublicPreviewController uses a FileDisplayResponse to serve the file preview with proper cache control.
Something similar would be nice for the ShareController. That is, instead of forbidding caching in sendHeaders, set the last-modified header to the file modification date and possibly set an etag, and return a 304 response in getSingleFile when appropriate.
Describe alternatives you've considered
My use case is to embed shared files in web pages. The alternative would be to set up a web server to serve static files. Using Nextcloud to do so is not very efficient, but can be handy. For this, it would be useful to avoid needless re-requests of downloads by clients.
Additional context
Simulation of getting a preview URL for the first time:
$ curl -I 'https://SERVER/index.php/apps/files_sharing/publicpreview/SHAREID?file=/&fileId=FILEID&x=1920&y=1200&a=true&etag=ETAG'
HTTP/2 200
date: Fri, 17 May 2024 17:38:01 GMT
pragma: no-cache
x-request-id: ZkeV-S6ejSRIelV3YpDT0wAAAQc
cache-control: private, max-age=86400, must-revalidate
content-security-policy: default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'
feature-policy: autoplay 'none';camera 'none';fullscreen 'none';geolocation 'none';microphone 'none';payment 'none'
x-robots-tag: noindex, nofollow
content-disposition: inline; filename="512-512-max.jpg"
expires: Sat, 18 May 2024 17:38:01 +0000
set-cookie: SOME COOKIES
referrer-policy: no-referrer
x-permitted-cross-domain-policies: none
x-xss-protection: 1; mode=block
last-modified: Fri, 17 May 2024 14:20:28 GMT
etag: "98ce490be53325b5f254d210cbab4624"
content-length: 60210
content-type: image/jpeg
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
strict-transport-security: max-age=31536000
Simulation of getting a preview URL for the second time:
$ curl -I 'https://SERVER/index.php/apps/files_sharing/publicpreview/SHAREID?file=/&fileId=FILEID&x=1920&y=1200&a=true&etag=ETAG' -H 'If-Modified-Since: Fri, 17 May 2024 17:38:01 GMT'
HTTP/2 304
(followed by all the same headers as before)
Getting a download URL:
$ curl -I 'https:/SERVER/index.php/s/SHAREID/download/test.jpg'
HTTP/2 200
date: Fri, 17 May 2024 17:41:41 GMT
content-security-policy: default-src 'self'; script-src 'self' 'nonce-NONCE'; style-src 'self' 'unsafe-inline'; frame-src *; img-src * data: blob:; font-src 'self' data:; media-src *; connect-src *; object-src 'none'; base-uri 'self';
pragma: no-cache
content-disposition: attachment; filename*=UTF-8''test.jpg; filename="test.jpg"
content-transfer-encoding: binary
expires: 0
cache-control: must-revalidate, post-check=0, pre-check=0
x-accel-buffering: no
set-cookie: SOME COOKIES
referrer-policy: no-referrer
x-permitted-cross-domain-policies: none
x-robots-tag: noindex, nofollow
x-xss-protection: 1; mode=block
content-length: 63834
content-type: image/jpeg
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
strict-transport-security: max-age=31536000
Adding an if-modified-since to the last request does not change the outcome.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with apps/files_sharing/lib/Controller/ShareController.php and the legacy lib/private/legacy/OC_Files.php, especially sendHeaders and getSingleFile. Compare their download response behavior with PublicPreviewController and FileDisplayResponse. Done means publicly shared downloads expose suitable modification or entity metadata and handle conditional requests with a 304 response where appropriate.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- php
- Domain
- backend
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100