17👍
From the documentation:
By default, if an uploaded file is smaller than 2.5 megabytes, Django will hold the entire contents of the upload in memory.
In more concrete terms, it means smaller files use the MemoryFileUploadHandler while larger files use the TemporaryFileUploadHandler. The latter uses tempfile
to create a temporary file with user-only access.
After going through all form and model validation and everything, the actual saving is performed by FileSystemStorage._save
method. At this point, the file is still either a TemporaryUploadedFile
or a InMemoryUploadedFile
depending on its size.
Now, a TemporaryUploadedFile is an actual file, created by tempfile
, with user-only permissions.
The save method does the smart thing: if given a temporary file (namely, if hasattr(content, 'temporary_file_path')
), it moves it instead of copying it. This means it keeps its user-only permissions and remains unreadable by www-data
.
The problem doesn’t show up with InMemoryUploadedFile, which will simply use whatever default permissions the process has (in your case, read/write for both user and group).
How to fix?
The storage object can set the permissions if so requested. For the default storage object, you can set this using FILE_UPLOAD_PERMISSIONS
. Here,
FILE_UPLOAD_PERMISSIONS=0o640
…should do the trick.
(that’s R/W for django user and read-only for nginx)