diff --git a/core/modules/file/src/Element/ManagedFile.php b/core/modules/file/src/Element/ManagedFile.php
index ca4e887a1b..d748e4c703 100644
--- a/core/modules/file/src/Element/ManagedFile.php
+++ b/core/modules/file/src/Element/ManagedFile.php
@@ -313,6 +313,26 @@ public static function processManagedFile(&$element, FormStateInterface $form_st
       $element['upload']['#attributes'] = ['accept' => $element['#accept']];
     }
 
+    // Forward #required to file input if multiple values are not allowed.
+    // We don't do this for multiple value field as it is more complex and
+    // should get validated on server side.
+    if (isset($element['#required']) && $element['#required'] === TRUE && (!isset($element['#multiple']) || empty($element['#multiple']))) {
+      $element['upload']['#attributes']['aria-required'] = 'true';
+      $element['upload']['#attributes']['required'] = 'required';
+
+      // We have to use required_error here as we want to use the title from
+      // main field and not file input.
+      // If element title is not set, we will use generic message.
+      if (isset($element['#title'])) {
+        $element['upload']['#required_error'] = t('@title is required.', [
+          '@title' => $element['#title'],
+        ]);
+      }
+      else {
+        $element['upload']['#required_error'] = t('This field is required.');
+      }
+    }
+
     if (!empty($fids) && $element['#files']) {
       foreach ($element['#files'] as $delta => $file) {
         $file_link = [
