diff --git a/owl/course.py b/owl/course.py
index 43f333d851dd4b377f6924e1da7df285926d23d6..72deb1aa11fd33542dc391af5b04898e7f6432a8 100644
--- a/owl/course.py
+++ b/owl/course.py
@@ -5,7 +5,7 @@ import datetime
 from decimal import Decimal
 from flask import Flask, render_template, request, make_response, g, request_tearing_down, redirect, url_for, flash
 from flask_wtf import FlaskForm
-from sqlalchemy import select, and_, or_, false
+from sqlalchemy import select, and_, or_, false, delete
 import sqlalchemy.sql.functions as func
 from sqlalchemy.orm import joinedload, aliased
 from typing import Optional, Tuple
@@ -215,6 +215,40 @@ def enroll_commit(course: db.Course) -> None:
     sess.commit()
 
 
+# Leaving
+
+class LeaveConfirmForm(FlaskForm):
+    pass
+
+
+@app.route('/c/<sident>/<cident>/leave', methods=('GET', 'POST'))
+def leave(sident: str, cident: str) -> str:
+    app.course_init(sident, cident)
+    sess = db.get_session()
+
+    err = None
+    if g.is_teacher:
+        err = 'Teachers are not allowed to leave their courses.'
+    else:
+        if sess.scalar(
+            select(db.Post)
+            .join(db.Topic)
+            .filter(or_(db.Post.target_uid == g.uid, db.Post.author_uid == g.uid))
+            .filter(db.Topic.course == g.course)
+            .limit(1)
+        ):
+            err = 'You cannot leave, because you already have posts in this course.'
+
+    form = LeaveConfirmForm()
+    if err is None and form.validate_on_submit():
+        sess.execute(delete(db.Enroll).where(and_(db.Enroll.uid == g.uid, db.Enroll.course == g.course)))
+        sess.commit()
+        flash(f'You left the course {g.course.name}.', 'info')
+        return redirect(url_for('index'))
+
+    return render_template('leave.html', form=form, error=err)
+
+
 # Topics
 
 @app.route('/c/<sident>/<cident>/<tident>/', methods=('GET', 'POST'))
diff --git a/owl/templates/course.html b/owl/templates/course.html
index afd17f76f66123de8fdc2eccba359a28be1afc7f..07e2168c2eb3fb287aa30aeb6f9b7d041b3c779d 100644
--- a/owl/templates/course.html
+++ b/owl/templates/course.html
@@ -84,4 +84,9 @@
 	{% else %}
 		<p>The course is empty. Come back in a couple of millifortnights.
 	{% endif %}
+	{% if not g.is_teacher %}
+		<nav class=buttons>
+			<a class=button href='{{ url_for('leave', sident=g.course.semester.ident, cident=g.course.ident) }}'>Leave the course</a>
+		</nav>
+	{% endif %}
 {% endblock %}
diff --git a/owl/templates/leave.html b/owl/templates/leave.html
new file mode 100644
index 0000000000000000000000000000000000000000..6ad17195c0bc34863fcc31d874a05d3bc5ce36bf
--- /dev/null
+++ b/owl/templates/leave.html
@@ -0,0 +1,21 @@
+{% set title = g.course.name %}
+{% extends "base.html" %}
+{% block body %}
+	<h2>{{ g.course.name }}</h2>
+
+	{% if error %}
+		<p class="flash flash-error">{{ error }}
+	{% else %}
+		<p>Please confirm that you want to leave this course.
+	{% endif %}
+
+	<nav class=buttons>
+		{% if not error %}
+			<form method="POST" action="?">
+				{{ form.csrf_token }}
+				<input class=danger type=submit value='Leave'>
+			</form>
+		{% endif %}
+		<a class=button href='{{ url_for('course_index', sident=g.course.semester.ident, cident=g.course.ident) }}'>Back to the course</a>
+	</nav>
+{% endblock %}