nextcloud / nextcloud/server

Allow web browsers to cache publicly shared files

Open
#45,399 1 comment 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

0. Needs triage enhancement feature: sharing hotspot: file transfer performance performance 🚀
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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.