diff --git a/src/core/markdown.py b/src/core/markdown.py
index 95246a86fbfc492844a74c759c3ebdd1b1c4ebb3..685481c9b9180f52cb5416711b4c70699a9a8b0a 100644
--- a/src/core/markdown.py
+++ b/src/core/markdown.py
@@ -3,7 +3,7 @@ import re
 
 import bleach
 import mistletoe
-from mistletoe.block_token import BlockToken
+from mistletoe.block_token import BlockToken, Heading
 from mistletoe.html_renderer import HTMLRenderer
 from mistletoe.span_token import AutoLink, Link, SpanToken, tokenize_inner
 
@@ -118,6 +118,11 @@ class MyHtmlRenderer(HTMLRenderer):
         inner = self.render_inner(token)
         return template.format(target=target, title=title, inner=inner, link_type=link_type)
 
+    def render_heading(self, token: Heading) -> str:
+        content = ' '.join([child.content for child in token.children])
+        heading = f'<h{token.level} id="{slugify(content)}">{content}</h{token.level}>'
+        return heading
+
     def render_auto_link(self, token: AutoLink) -> str:
         link_type, url = resolve_link(token.target)
         self.result.linked_urls.add(url)
diff --git a/src/core/tests/markdown.py b/src/core/tests/markdown.py
index de008cd8a96d7a1172c716c8b6328b276a1fec65..9b6b0be7b2244c67bfe524ca80efe86c8d18065c 100644
--- a/src/core/tests/markdown.py
+++ b/src/core/tests/markdown.py
@@ -135,3 +135,12 @@ class MarkdownTest(TestCase):
 
         # triple sanity check: we expected four links total (one of them not to a user)
         self.assertEqual(4, len(re.findall('<a ', md_rendered)))
+
+    @override_settings(SELECTED_CONFERENCE_ID=TEST_CONF_ID)
+    def test_headings(self):
+        conf = Conference(name='foo', id=TEST_CONF_ID)
+        conf.save()
+
+        self.assertEqual(render_markdown(conf, '# Heading 1'), '<h1 id="heading-1">Heading 1</h1>\n')
+        self.assertEqual(render_markdown(conf, '## Heading with Ü'), '<h2 id="heading-with-u">Heading with Ü</h2>\n')
+        self.assertEqual(render_markdown(conf, 'Multi\nline\n======'), '<h1 id="multi-line">Multi  line</h1>\n')