diff --git a/server.py b/server.py
index 1b087d10a84e7900492300bf15f27ba06f6ba64e..57bfc4ff335f998198e76eff54cc832f4c9ce90c 100755
--- a/server.py
+++ b/server.py
@@ -8,6 +8,7 @@ from flask_script import Manager, prompt
 from flask_migrate import Migrate, MigrateCommand
 #from flask_socketio import SocketIO
 from celery import Celery
+from sqlalchemy import or_, and_
 from io import StringIO, BytesIO
 import os
 from datetime import datetime
@@ -297,24 +298,74 @@ def list_protocols():
     if search_term is not None:
         search_form.search.data = search_term
     protocol_query = Protocol.query
-    if search_term is not None:
-        match_target = Protocol.content_public
-        if protocoltype is not None and protocoltype.has_private_view_right(user):
-            match_target = Protocol.content_private
-        for term in split_terms(search_term):
-            protocol_query = protocol_query.filter(match_target.match("%{}%".format(term)))
+    shall_search = search_term is not None and len(search_term.strip()) > 0
+    search_terms = []
+    if shall_search:
+        search_terms = list(map(str.lower, split_terms(search_term)))
+        for term in search_terms:
+            protocol_query = protocol_query.filter(or_(
+                Protocol.content_public.ilike("%{}%".format(term)),
+                Protocol.content_private.ilike("%{}%".format(term))
+            ))
     protocols = [
         protocol for protocol in protocol_query.all()
         if (not is_logged_in and protocol.protocoltype.is_public)
         or (is_logged_in and (
             protocol.protocoltype.public_group in user.groups
             or protocol.protocoltype.private_group in user.groups))]
+    def _matches_search(content):
+        content = content.lower()
+        for search_term in search_terms:
+            if search_term.lower() not in content:
+                return False
+        return True
+    def _matches_search_lazy(content):
+        content = content.lower()
+        for search_term in search_terms:
+            if search_term.lower() in content:
+                return True
+        return False
+    search_results = {} if shall_search else None
     if protocoltype_id is not None and protocoltype_id != -1:
         protocols = [
             protocol for protocol in protocols
             if protocol.protocoltype.id == protocoltype_id
         ]
-    protocols = sorted(protocols, key=lambda protocol: protocol.date, reverse=True)
+    if shall_search:
+        protocols = [
+            protocol for protocol in protocols
+            if (protocol.protocoltype.has_private_view_right(user)
+                and _matches_search(protocol.content_private))
+            or (protocol.protocoltype.has_public_view_right(user)
+                and _matches_search(protocol.content_public))
+        ]
+        for protocol in protocols:
+            content = protocol.content_private if protocol.protocoltype.has_private_view_right(user) else protocol.content_public
+            lines = content.splitlines()
+            matches = [line for line in lines if _matches_search_lazy(line)]
+            formatted_lines = []
+            for line in matches:
+                parts = []
+                lower_line = line.lower()
+                last_index = 0
+                while last_index < len(line):
+                    index_candidates = list(filter(lambda t: t[0] != -1, 
+                        [(lower_line.find(term, last_index), term) for term in search_terms]))
+                    if len(index_candidates) == 0:
+                        parts.append((line[last_index:], False))
+                        break
+                    else:
+                        new_index, term = min(index_candidates, key=lambda t: t[0])
+                        new_end_index = new_index + len(term)
+                        parts.append((line[last_index:new_index], False))
+                        parts.append((line[new_index:new_end_index], True))
+                        last_index = new_end_index
+                formatted_lines.append("".join([
+                    "<b>{}</b>".format(text) if matched else text
+                    for text, matched in parts
+                ]))
+            search_results[protocol] = "<br />\n".join(formatted_lines)
+        protocols = sorted(protocols, key=lambda protocol: protocol.date, reverse=True)
     page = _get_page()
     page_count = int(math.ceil(len(protocols)) / config.PAGE_LENGTH)
     if page >= page_count:
@@ -322,7 +373,7 @@ def list_protocols():
     begin_index = page * config.PAGE_LENGTH
     end_index = (page + 1) * config.PAGE_LENGTH
     protocols = protocols[begin_index:end_index]
-    protocols_table = ProtocolsTable(protocols)
+    protocols_table = ProtocolsTable(protocols, search_results=search_results)
     return render_template("protocols-list.html", protocols=protocols, protocols_table=protocols_table, search_form=search_form, page=page, page_count=page_count, page_diff=config.PAGE_DIFF, protocoltype_id=protocoltype_id, search_term=search_term)
 
 @app.route("/protocol/new", methods=["GET", "POST"])
diff --git a/views/tables.py b/views/tables.py
index 778e4235e3c9ae73ab4748a366e6e8e47c869bab..4a44cbb589844a58b7658979426f340b64eab987 100644
--- a/views/tables.py
+++ b/views/tables.py
@@ -48,14 +48,21 @@ class SingleValueTable:
         return [self.row()]
 
 class ProtocolsTable(Table):
-    def __init__(self, protocols):
+    def __init__(self, protocols, search_results=None):
         super().__init__("Protokolle", protocols, newlink=url_for("new_protocol"))
+        self.search_results = search_results
 
     def headers(self):
-        result = ["ID", "Sitzung", "Datum", "Status"]
-        optional_part = ["Typ", "Löschen"]
+        result = ["ID", "Sitzung", "Datum"]
+        state_part = ["Status"]
+        search_part = ["Suchergebnis"]
+        login_part = ["Typ", "Löschen"]
+        if self.search_results is None:
+            result.extend(state_part)
+        else:
+            result.extend(search_part)
         if check_login():
-            result += optional_part
+            result.extend(login_part)
         return result
 
     def row(self, protocol):
@@ -64,8 +71,11 @@ class ProtocolsTable(Table):
             Table.link(url_for("show_protocol", protocol_id=protocol.id), str(protocol.id)),
             Table.link(url_for("show_protocol", protocol_id=protocol.id), protocol.protocoltype.name),
             date_filter(protocol.date),
-            "Fertig" if protocol.is_done() else "Geplant"
         ]
+        if self.search_results is None:
+            result.append("Fertig" if protocol.is_done() else "Geplant")
+        elif protocol in self.search_results:
+            result.append(Markup(self.search_results[protocol]))
         if user is not None and protocol.protocoltype.has_private_view_right(user):
             result.append(Table.link(url_for("show_type", type_id=protocol.protocoltype.id), protocol.protocoltype.short_name))
             result.append(Table.link(url_for("delete_protocol", protocol_id=protocol.id), "Löschen", confirm="Bist du dir sicher, dass du das Protokoll {} löschen möchtest?".format(protocol.get_identifier())))