Let's say I have a node type "Article" which has a field "File" which is set to handle public files. A lot of articles and files get added.

Now the specs suddenly change and the "Article" will move behind a login and therefore also the attached files must be changed to be not publicly accessible anymore.

I already found out how to change the file system for the file field from public to private pretty easily by reimporting the manually updated field.storage.node.field_file.yml config. But: this only affects new files that will be added after the switch. It doesn't work for existing files. They still are accessible publicly. Many thousand existing files would need to be manually reuploaded to also make them private.

Does anybody of you have solved that in D8 before? If so, then how?

As all I can think of right now is to programmatically query all "Article" nodes, then foreach through all nodes, get the files, temporarily store them somewhere else, empty the field out, then try to reupload the files programmatically (probably my next question) and then resave each node.

Does that sound reasonable? Or can you think of any other approach that might work here (in D8)?


Thanks a lot for the answers! FileField Paths definitely is the way to go.

  1. Update the file field's field storage to handle private files.
  2. Run FileField Paths' Retroactive update on your content type's file field.

Step 1. can be accomplished from within hook_update_N (if you use configuration management do that locally and then export the updated configuration and deploy the resulting *.yml):

$field_type = 'file';
$field_name = 'field_file';

if (!$field_storage_configs = \Drupal::entityManager()
  ->loadByProperties(['type' => $field_type])) {

foreach ($field_storage_configs as $field_storage) {
  // Note: These settings apply to the field_file field everywhere it is
  // used.
  if ($field_storage->getName() == $field_name) {
    $field_storage->setSetting('uri_scheme', 'private');

Alternatively, follow the manual config update and reimport as suggested in the question.

Step 2. is to go to your file field's edit screen and fire the Retroactive update upon saving the form:

enter image description here


It's quite simple, I tested this myself and it works.

1) With PHPMyAdmin or some DB managing application, you'll need to look at node__field_NAME that has your files. Record all the field_NAME_target_id numbers that belong to your content type.

2) Go to to file_managed table.

3) Export table as .sql file

4) Open .sql file with some text editing app. Rename all public:// to private:// for the ones that have the fid number that you recorded.

5) Import .sql and override table file_managed. OR you could clear the table and then import.

6) Move the files to the private folder.

  • Important: make sure that it has the same parent folder structure. If it was previously under /files/2018-06/some.jpg, in private it needs to be /private/2018-06/some.jpg

    • Also, no need to move image style image files, these will get auto created. Thus, you only need to grab the original.

7) Flush all caches.

I'm sure you could use SQL query to do 1 to 5. Basically all you gotta do is update the specific values of the uri column in the field_managed table.

  • Thanks a lot for your detailed answer! As always a pleasure. Although I must admit I find it gets quite complex pretty fast when you want to automate that.
    – leymannx
    Jul 10 '18 at 12:25
  • 1
    Or just do a query: UPDATE file_managed SET file_managed.uri = REPLACE(file_managed.uri, 'public://', 'private://');
    – mbomb007
    Nov 12 '20 at 21:17

I did this for Drupal 7, but haven't tried it in Drupal 8.

I believe that it should work similarly since there is already a D8 release for the required module: FileField Paths. I first installed and enabled the module before following the steps below:

just in case someone is looking to do this, here are the steps:

  1. backup your site and db
  2. copy the table for your field to make a backup (in my case i copied: field_data_field_attachments to field_data_field_attachments_bk)
  3. empty table field_data_field_attachments
  4. go to drupal and the option to change the file system should be available, so change it
  5. copy all data from field_data_field_attachments_bk back to field_data_field_attachments
  6. go to drupal, select your content type, select your file field, in file path settings select retroactive update
  7. click save and it will move all your files to the right place in file system and update your db to the new path

A Romka

Full thread

  • 1
    FileField Paths definitely is the way to go! Though steps 1 to 5 are invalid in D8.
    – leymannx
    Jul 10 '18 at 12:15
  • You don't have to empty the table to change the file system setting. For Drupal 7, you can just run this (this is for all fields, you can change the WHERE for a specific one): UPDATE field_config SET data = REPLACE(field_config.data, 's:10:"uri_scheme";s:6:"public";', 's:10:"uri_scheme";s:7:"private";') WHERE data LIKE '%s:10:"uri_scheme";s:6:"public";%';
    – mbomb007
    Nov 12 '20 at 16:24
  • FileField Paths didn't work for me. No idea why.
    – mbomb007
    Nov 12 '20 at 22:40

With my Drupal 8.6.3 the retro-active update doesn't work.

You still need to achieve step 1, to update the field storage of your field.

Then you need to update all existing files via the database with:

UPDATE file_managed SET uri = REPLACE( 
WHERE filemime like 'application%';

In my case I'm updating only the files, that's why I'm using this where clause.

Don't forget to clear the cache after this SQL query.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.