diff --git a/lib/grade/grade_category.php b/lib/grade/grade_category.php
index 207f3fc1cea..f4a195eb2c6 100644
--- a/lib/grade/grade_category.php
+++ b/lib/grade/grade_category.php
@@ -472,9 +472,10 @@ class grade_category extends grade_object {
      *  4. Save them in final grades of associated category grade item
      *
      * @param int $userid The user ID if final grade generation should be limited to a single user
+     * @param \core\progress\base|null $progress Optional progress indicator
      * @return bool
      */
-    public function generate_grades($userid=null) {
+    public function generate_grades($userid=null, ?\core\progress\base $progress = null) {
         global $CFG, $DB;
 
         $this->load_grade_item();
@@ -565,6 +566,12 @@ class grade_category extends grade_object {
                 if ($this->grade_item->id == $grade->itemid) {
                     $oldgrade = $grade;
                 }
+
+                if ($progress) {
+                    // Incrementing the progress by nothing causes it to send an update (once per second)
+                    // to the web browser so as to prevent the connection timing out.
+                    $progress->increment_progress(0);
+                }
             }
             $this->aggregate_grades($prevuser,
                                     $items,
diff --git a/lib/grade/grade_item.php b/lib/grade/grade_item.php
index 1ad17784912..98548d43cac 100644
--- a/lib/grade/grade_item.php
+++ b/lib/grade/grade_item.php
@@ -781,9 +781,10 @@ class grade_item extends grade_object {
      * because the regrading must be done in correct order!!
      *
      * @param int $userid Supply a user ID to limit the regrading to a single user
+     * @param \core\progress\base|null $progress Optional progress object, will be updated per user
      * @return bool true if ok, error string otherwise
      */
-    public function regrade_final_grades($userid=null) {
+    public function regrade_final_grades($userid=null, ?\core\progress\base $progress = null) {
         global $CFG, $DB;
 
         // locked grade items already have correct final grades
@@ -808,7 +809,7 @@ class grade_item extends grade_object {
             // aggregate category grade item
             $category = $this->load_item_category();
             $category->grade_item =& $this;
-            if ($category->generate_grades($userid)) {
+            if ($category->generate_grades($userid, $progress)) {
                 return true;
             } else {
                 return "Could not aggregate final grades for category:".$this->id; // TODO: improve and localize
@@ -837,6 +838,12 @@ class grade_item extends grade_object {
             foreach ($rs as $grade_record) {
                 $grade = new grade_grade($grade_record, false);
 
+                // Incrementing the progress by nothing causes it to send an update (once per second)
+                // to the web browser so as to prevent the connection timing out.
+                if ($progress) {
+                    $progress->increment_progress(0);
+                }
+
                 if (!empty($grade_record->locked) or !empty($grade_record->overridden)) {
                     // this grade is locked - final grade must be ok
                     continue;
diff --git a/lib/gradelib.php b/lib/gradelib.php
index 8dc9ebd5204..3f941e0a3cc 100644
--- a/lib/gradelib.php
+++ b/lib/gradelib.php
@@ -1275,7 +1275,7 @@ function grade_regrade_final_grades($courseid, $userid=null, $updated_item=null,
             }
 
             // Let's update, calculate or aggregate.
-            $result = $grade_items[$gid]->regrade_final_grades($userid);
+            $result = $grade_items[$gid]->regrade_final_grades($userid, $progress);
 
             if ($result === true) {