File field download method: public files remain directly reachable after change
0 reputation · 27 Nov 2020, 21:17 UTC
0 reputation · 27 Nov 2020, 21:17 UTC
Drupal supports two documented download methods for file and image fields: public files served directly by the web server from the public files directory, and private files streamed through Drupal's file download route with entity access checks.
The private file system is enabled via $settings['file_private_path'] in settings.php and is typically placed outside the web root. Download method is a per-field setting. Changing the method later does not rewrite URIs of files already uploaded, so existing public:// files remain directly reachable until migrated. Core does not provide a built-in bulk migration for this.
Access to private files is derived from the entities that reference them and can be altered via file download access hooks. Protection also depends on server configuration when a private directory must reside inside the web root, as .htaccess rules are not honored by all web servers and proxies.
With these constraints, the unresolved decision is how to ensure sensitive uploads are not accidentally public and what to do with files already stored in the public files directory.
Does changing a file field's download method to private automatically restrict existing public:// URIs? What documented behavior governs access checks for private files streamed via Drupal's file download route? How does server configuration affect protection when the private path must reside inside the web root?
Changing a file field's download method to Private does not automatically restrict existing files. Because Drupal does not retroactively rewrite the URIs of previously uploaded files, any file already stored with a public:// scheme remains directly reachable via its web-accessible URL, bypassing Drupal's access control system entirely.
When a file is correctly assigned the private:// scheme, Drupal handles the request differently than it does for public files:
system/files (file download) route.hook_file_download() to implement custom logic, returning -1 to deny access or 0 to allow it.The security of private files relies on the file system's inability to be accessed directly by a browser. If your $settings['file_private_path'] is located inside the web root, Drupal's internal access checks are insufficient on their own.
In this scenario, the web server must be explicitly configured to block direct requests to that directory. For example, Apache uses .htaccess rules, while Nginx requires a location block to return a 403 Forbidden or 404 Not Found. If these server-level rules are missing or ignored by a proxy/CDN, the files remain public regardless of the Drupal field setting.
To ensure sensitive uploads are protected after changing the field setting, you must manually migrate existing files:
uri column in the file_managed table from public://path/to/file to private://path/to/file.$settings['file_private_path'].system/files route returns a 403 for unauthorized users.Diagnostic Detail: Please specify your web server (e.g., Nginx, Apache) and whether your private path is currently outside the document root, as this determines which server-level blocks are required.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.