diff --git a/core/src/main/java/se/su/dsv/scipro/forum/BasicForumService.java b/core/src/main/java/se/su/dsv/scipro/forum/BasicForumService.java
index 4bccf27413..6a680ad26f 100644
--- a/core/src/main/java/se/su/dsv/scipro/forum/BasicForumService.java
+++ b/core/src/main/java/se/su/dsv/scipro/forum/BasicForumService.java
@@ -1,7 +1,8 @@
 package se.su.dsv.scipro.forum;
 
 import java.io.Serializable;
-import java.util.*;
+import java.util.List;
+import java.util.Set;
 import se.su.dsv.scipro.forum.dataobjects.ForumPost;
 import se.su.dsv.scipro.forum.dataobjects.ForumThread;
 import se.su.dsv.scipro.system.User;
@@ -20,4 +21,6 @@ public interface BasicForumService extends Serializable {
     List<ForumPost> getPosts(ForumThread forumThread);
 
     ForumThread createThread(String subject);
+
+    long countUnreadThreads(List<ForumThread> forumThreadList, User user);
 }
diff --git a/core/src/main/java/se/su/dsv/scipro/forum/BasicForumServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/forum/BasicForumServiceImpl.java
index 8da3bb70d6..c48e3cab91 100644
--- a/core/src/main/java/se/su/dsv/scipro/forum/BasicForumServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/forum/BasicForumServiceImpl.java
@@ -87,6 +87,11 @@ public class BasicForumServiceImpl implements BasicForumService {
         return threadRepository.save(forumThread);
     }
 
+    @Override
+    public long countUnreadThreads(List<ForumThread> forumThreadList, User user) {
+        return postRepository.countUnreadThreads(forumThreadList, user);
+    }
+
     private ForumPostReadState getReadState(final User user, final ForumPost post) {
         ForumPostReadState state = readStateRepository.find(user, post);
         if (state == null) {
diff --git a/core/src/main/java/se/su/dsv/scipro/forum/ForumPostRepository.java b/core/src/main/java/se/su/dsv/scipro/forum/ForumPostRepository.java
index f557fd70a6..e5d975add4 100644
--- a/core/src/main/java/se/su/dsv/scipro/forum/ForumPostRepository.java
+++ b/core/src/main/java/se/su/dsv/scipro/forum/ForumPostRepository.java
@@ -8,6 +8,7 @@ import se.su.dsv.scipro.forum.dataobjects.ProjectThread;
 import se.su.dsv.scipro.project.Project;
 import se.su.dsv.scipro.system.JpaRepository;
 import se.su.dsv.scipro.system.QueryDslPredicateExecutor;
+import se.su.dsv.scipro.system.User;
 import se.su.dsv.scipro.util.Pair;
 
 @Transactional
@@ -15,4 +16,6 @@ public interface ForumPostRepository extends JpaRepository<ForumPost, Long>, Que
     List<ForumPost> findByThread(ForumThread forumThread);
 
     List<Pair<ProjectThread, ForumPost>> latestPost(Project project, int amount);
+
+    long countUnreadThreads(List<ForumThread> forumThreadList, User user);
 }
diff --git a/core/src/main/java/se/su/dsv/scipro/forum/ForumPostRepositoryImpl.java b/core/src/main/java/se/su/dsv/scipro/forum/ForumPostRepositoryImpl.java
index 207d02c9d5..f43a0147bb 100644
--- a/core/src/main/java/se/su/dsv/scipro/forum/ForumPostRepositoryImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/forum/ForumPostRepositoryImpl.java
@@ -2,19 +2,22 @@ package se.su.dsv.scipro.forum;
 
 import static com.querydsl.core.types.dsl.Expressions.allOf;
 
+import com.querydsl.jpa.JPAExpressions;
 import com.querydsl.jpa.impl.JPAQuery;
 import jakarta.inject.Inject;
 import jakarta.inject.Provider;
 import jakarta.persistence.EntityManager;
-import java.util.*;
+import java.util.List;
 import se.su.dsv.scipro.forum.dataobjects.ForumPost;
 import se.su.dsv.scipro.forum.dataobjects.ForumThread;
 import se.su.dsv.scipro.forum.dataobjects.ProjectThread;
 import se.su.dsv.scipro.forum.dataobjects.QForumPost;
+import se.su.dsv.scipro.forum.dataobjects.QForumPostReadState;
 import se.su.dsv.scipro.forum.dataobjects.QForumThread;
 import se.su.dsv.scipro.forum.dataobjects.QProjectThread;
 import se.su.dsv.scipro.project.Project;
 import se.su.dsv.scipro.system.GenericRepo;
+import se.su.dsv.scipro.system.User;
 import se.su.dsv.scipro.util.Pair;
 
 public class ForumPostRepositoryImpl extends GenericRepo<ForumPost, Long> implements ForumPostRepository {
@@ -44,4 +47,24 @@ public class ForumPostRepositoryImpl extends GenericRepo<ForumPost, Long> implem
             .map(tuple -> new Pair<>(tuple.get(QProjectThread.projectThread), tuple.get(QForumPost.forumPost)))
             .toList();
     }
+
+    @Override
+    public long countUnreadThreads(List<ForumThread> forumThreadList, User user) {
+        return new JPAQuery<>(em())
+            .select(QForumThread.forumThread.id.countDistinct())
+            .from(QForumThread.forumThread)
+            .leftJoin(QForumThread.forumThread.posts, QForumPost.forumPost)
+            .where(
+                QForumPost.forumPost.notIn(
+                    JPAExpressions.select(QForumPostReadState.forumPostReadState.id.post)
+                        .from(QForumPostReadState.forumPostReadState)
+                        .where(
+                            QForumPostReadState.forumPostReadState.read.isTrue(),
+                            QForumPostReadState.forumPostReadState.id.user.eq(user)
+                        )
+                ),
+                QForumThread.forumThread.in(forumThreadList)
+            )
+            .fetchOne();
+    }
 }
diff --git a/core/src/main/java/se/su/dsv/scipro/forum/ProjectForumService.java b/core/src/main/java/se/su/dsv/scipro/forum/ProjectForumService.java
index c8f9c3e020..a6ac11ebcd 100644
--- a/core/src/main/java/se/su/dsv/scipro/forum/ProjectForumService.java
+++ b/core/src/main/java/se/su/dsv/scipro/forum/ProjectForumService.java
@@ -23,5 +23,5 @@ public interface ProjectForumService {
     // TODO: Get these away from here
     List<Pair<ProjectThread, ForumPost>> latestPost(Project a, int amount);
 
-    boolean hasUnreadThreads(Project project, User user);
+    long getUnreadThreadsCount(Project project, User user);
 }
diff --git a/core/src/main/java/se/su/dsv/scipro/forum/ProjectForumServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/forum/ProjectForumServiceImpl.java
index 5de733222a..0c6b65227f 100644
--- a/core/src/main/java/se/su/dsv/scipro/forum/ProjectForumServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/forum/ProjectForumServiceImpl.java
@@ -3,7 +3,8 @@ package se.su.dsv.scipro.forum;
 import com.google.common.eventbus.EventBus;
 import jakarta.inject.Inject;
 import jakarta.transaction.Transactional;
-import java.util.*;
+import java.util.List;
+import java.util.Set;
 import se.su.dsv.scipro.file.FileSource;
 import se.su.dsv.scipro.file.ProjectFileService;
 import se.su.dsv.scipro.forum.dataobjects.ForumPost;
@@ -114,14 +115,10 @@ public class ProjectForumServiceImpl implements ProjectForumService {
     }
 
     @Override
-    public boolean hasUnreadThreads(Project project, User user) {
+    public long getUnreadThreadsCount(Project project, User user) {
         List<ProjectThread> threads = getThreads(project);
-        for (ProjectThread thread : threads) {
-            if (!basicForumService.isThreadRead(user, thread.getForumThread())) {
-                return true;
-            }
-        }
-        return false;
+        List<ForumThread> list = threads.stream().map(ProjectThread::getForumThread).toList();
+        return basicForumService.countUnreadThreads(list, user);
     }
 
     @Override
diff --git a/core/src/test/java/se/su/dsv/scipro/forum/ProjectForumServiceImplTest.java b/core/src/test/java/se/su/dsv/scipro/forum/ProjectForumServiceImplTest.java
index 3f6a99dba8..321bc5b940 100644
--- a/core/src/test/java/se/su/dsv/scipro/forum/ProjectForumServiceImplTest.java
+++ b/core/src/test/java/se/su/dsv/scipro/forum/ProjectForumServiceImplTest.java
@@ -105,9 +105,9 @@ public class ProjectForumServiceImplTest extends ForumModuleTest {
         final ProjectThread thread = service.createThread(project, supervisor, "subject", "content", Set.of());
         service.createReply(thread, author, "reply", Set.of());
 
-        boolean hasUnreadThreads = service.hasUnreadThreads(project, supervisor);
+        long count = service.getUnreadThreadsCount(project, supervisor);
 
-        assertTrue(hasUnreadThreads);
+        assertEquals(1, count);
     }
 
     private void assertNewForumThread(
diff --git a/owasp.xml b/owasp.xml
index c504d51e0f..125e5e37f6 100644
--- a/owasp.xml
+++ b/owasp.xml
@@ -72,4 +72,22 @@
         </notes>
         <cve>CVE-2024-23076</cve>
     </suppress>
+    <suppress>
+        <notes>
+            https://nvd.nist.gov/vuln/detail/CVE-2024-49203
+            https://github.com/querydsl/querydsl/issues/3757
+
+            Basically if you allow untrusted user input to be used in the "ORDER BY" clause
+            you can be vulnerable to SQL injection.
+
+            I believe this is nonsense and akin to saying every Java application has a
+            security vulnerability because JDBC allows you to execute arbitrary SQL if you
+            do not properly use PreparedStatement with parameters over a string-concatenated
+            Statement.
+
+            Even if this is considered a valid vulnerability we do not, currently, allow
+            untrusted user input to be used in the "ORDER BY" clause.
+        </notes>
+        <cve>CVE-2024-49203</cve>
+    </suppress>
 </suppressions>
diff --git a/view/src/main/java/se/su/dsv/scipro/forum/panels/AbstractReadStatePanel.html b/view/src/main/java/se/su/dsv/scipro/forum/panels/AbstractReadStatePanel.html
index 7e4d838121..0fb10bd9e4 100644
--- a/view/src/main/java/se/su/dsv/scipro/forum/panels/AbstractReadStatePanel.html
+++ b/view/src/main/java/se/su/dsv/scipro/forum/panels/AbstractReadStatePanel.html
@@ -6,9 +6,7 @@
 </head>
 <body>
 <wicket:panel>
-    <a wicket:id="toggle" href="#">
-        <span wicket:id="icon" class="fa fa-flag read-state"></span>
-    </a>
+    <a wicket:id="toggle" href="#"><span wicket:id="icon" class="fa fa-flag read-state"></span></a>
 </wicket:panel>
 </body>
 </html>
\ No newline at end of file
diff --git a/view/src/main/java/se/su/dsv/scipro/forum/panels/AbstractReadStatePanel.java b/view/src/main/java/se/su/dsv/scipro/forum/panels/AbstractReadStatePanel.java
index 92a80a83ef..54072c9532 100644
--- a/view/src/main/java/se/su/dsv/scipro/forum/panels/AbstractReadStatePanel.java
+++ b/view/src/main/java/se/su/dsv/scipro/forum/panels/AbstractReadStatePanel.java
@@ -10,10 +10,14 @@ import org.apache.wicket.markup.html.panel.Panel;
 
 public abstract class AbstractReadStatePanel extends Panel {
 
-    private final Component icon;
+    public static final String TOGGLE = "toggle";
+    static final String ICON = "icon";
 
     public AbstractReadStatePanel(final String id) {
         super(id);
+        Component icon = new UpdatingImage(ICON);
+        icon.setOutputMarkupId(true);
+
         AjaxFallbackLink<Void> link = new AjaxFallbackLink<>(TOGGLE) {
             @Override
             public void onClick(final Optional<AjaxRequestTarget> target) {
@@ -23,20 +27,15 @@ public abstract class AbstractReadStatePanel extends Panel {
                 });
             }
         };
-        add(link);
-
-        icon = new UpdatingImage(ICON);
-        icon.setOutputMarkupId(true);
         link.add(icon);
+
+        add(link);
     }
 
     protected abstract boolean isRead();
 
     protected abstract void onFlagClick(final AjaxRequestTarget target);
 
-    public static final String TOGGLE = "toggle";
-    static final String ICON = "icon";
-
     private class UpdatingImage extends WebComponent {
 
         public UpdatingImage(String id) {
diff --git a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorMyProjectsPanel.html b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorMyProjectsPanel.html
index 73ab571f7f..d171f8e3ba 100755
--- a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorMyProjectsPanel.html
+++ b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorMyProjectsPanel.html
@@ -41,6 +41,13 @@
     </div>
 
     <table class="table table-striped table-hover" wicket:id="dp"></table>
+
+    <wicket:fragment wicket:id="readStateColumnMarkupId">
+        <span wicket:id="flag"></span>
+        <wicket:enclosure child="counter">
+            (<wicket:container wicket:id="counter"></wicket:container>)
+        </wicket:enclosure>
+    </wicket:fragment>
 </wicket:panel>
 </body>
 </html>
\ No newline at end of file
diff --git a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorMyProjectsPanel.java b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorMyProjectsPanel.java
index 2b3c9dce94..cb76132906 100755
--- a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorMyProjectsPanel.java
+++ b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorMyProjectsPanel.java
@@ -5,6 +5,7 @@ import static java.util.Arrays.asList;
 import jakarta.inject.Inject;
 import java.util.ArrayList;
 import java.util.List;
+import org.apache.wicket.AttributeModifier;
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.ajax.form.AjaxFormChoiceComponentUpdatingBehavior;
 import org.apache.wicket.ajax.markup.html.form.AjaxCheckBox;
@@ -14,16 +15,22 @@ import org.apache.wicket.extensions.markup.html.repeater.data.table.AbstractColu
 import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
 import org.apache.wicket.extensions.markup.html.repeater.data.table.LambdaColumn;
 import org.apache.wicket.extensions.markup.html.repeater.util.SortableDataProvider;
+import org.apache.wicket.markup.html.basic.Label;
 import org.apache.wicket.markup.html.form.EnumChoiceRenderer;
 import org.apache.wicket.markup.html.form.Form;
 import org.apache.wicket.markup.html.form.LambdaChoiceRenderer;
+import org.apache.wicket.markup.html.panel.Fragment;
 import org.apache.wicket.markup.html.panel.Panel;
 import org.apache.wicket.markup.repeater.Item;
 import org.apache.wicket.model.IModel;
 import org.apache.wicket.model.LambdaModel;
 import org.apache.wicket.model.LoadableDetachableModel;
 import org.apache.wicket.model.Model;
-import se.su.dsv.scipro.components.*;
+import se.su.dsv.scipro.components.AjaxCheckBoxMultipleChoice;
+import se.su.dsv.scipro.components.BootstrapRadioChoice;
+import se.su.dsv.scipro.components.ExportableDataPanel;
+import se.su.dsv.scipro.components.ListAdapterModel;
+import se.su.dsv.scipro.components.TemporalColumn;
 import se.su.dsv.scipro.components.datatables.MultipleUsersColumn;
 import se.su.dsv.scipro.components.datatables.UserColumn;
 import se.su.dsv.scipro.dataproviders.FilteredDataProvider;
@@ -250,25 +257,49 @@ public class SupervisorMyProjectsPanel extends Panel {
 
         @Override
         public void populateItem(Item<ICellPopulator<Project>> item, String id, IModel<Project> projectModel) {
-            item.add(
-                new AbstractReadStatePanel(id) {
-                    @Override
-                    protected boolean isRead() {
-                        return !projectForumService.hasUnreadThreads(
-                            projectModel.getObject(),
-                            SciProSession.get().getUser()
-                        );
-                    }
+            // Since table cell only can contain one item, we use Wicket Fragment here. It will contain two components,
+            // one for flag, one for unread messages counter.
 
-                    @Override
-                    protected void onFlagClick(AjaxRequestTarget target) {
-                        setResponsePage(
-                            SupervisorThreadedForumPage.class,
-                            SupervisorThreadedForumPage.getPageParameters(projectModel.getObject())
-                        );
-                    }
-                }
+            Fragment fragment = new Fragment(id, "readStateColumnMarkupId", SupervisorMyProjectsPanel.this);
+
+            long msgCount = projectForumService.getUnreadThreadsCount(
+                projectModel.getObject(),
+                SciProSession.get().getUser()
             );
+            boolean isRead = msgCount == 0;
+
+            AbstractReadStatePanel readStatePanel = new AbstractReadStatePanel("flag") {
+                @Override
+                protected boolean isRead() {
+                    return isRead;
+                }
+
+                @Override
+                protected void onFlagClick(AjaxRequestTarget target) {
+                    setResponsePage(
+                        SupervisorThreadedForumPage.class,
+                        SupervisorThreadedForumPage.getPageParameters(projectModel.getObject())
+                    );
+                }
+            };
+
+            if (!isRead) {
+                readStatePanel.add(new AttributeModifier("title", getString("unread.msg")));
+            }
+
+            fragment.add(readStatePanel);
+
+            Label counterLabel = new Label("counter", msgCount) {
+                @Override
+                protected void onConfigure() {
+                    super.onConfigure();
+                    setVisible(msgCount > 0);
+                }
+            };
+
+            fragment.add(counterLabel);
+
+            item.add(fragment);
         }
     }
 }
diff --git a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorMyProjectsPanel.utf8.properties b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorMyProjectsPanel.utf8.properties
index 17f454ad8c..e279982933 100644
--- a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorMyProjectsPanel.utf8.properties
+++ b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorMyProjectsPanel.utf8.properties
@@ -12,3 +12,5 @@ ProjectStatus.COMPLETED= Completed
 
 SupervisorProjectNoteDisplay.COMPACT=Compact
 SupervisorProjectNoteDisplay.FULL=Full
+
+unread.msg=There are unread messages.
\ No newline at end of file