diff --git a/aleksis/core/__init__.py b/aleksis/core/__init__.py
index 21a49f0785f3977d9a265059a32ec957c9e1eb3c..587b829c611913af41c69f2734a48235956ff787 100644
--- a/aleksis/core/__init__.py
+++ b/aleksis/core/__init__.py
@@ -1,5 +1,7 @@
 import pkg_resources
 
+from django.utils.translation import gettext_lazy as _
+
 try:
     from .celery import app as celery_app
 except ModuleNotFoundError:
diff --git a/aleksis/core/apps.py b/aleksis/core/apps.py
index 13e3b3ddcf1083e1cff0f8c5e3461103bb12f4b2..3139249b79c2afb701ec9405eea5a6cec1a4526b 100644
--- a/aleksis/core/apps.py
+++ b/aleksis/core/apps.py
@@ -13,6 +13,20 @@ class CoreConfig(AppConfig):
     name = "aleksis.core"
     verbose_name = "AlekSIS — The Free School Information System"
 
+    urls = {
+        "Repository": "https://edugit.org/AlekSIS/official/AlekSIS/",
+    }
+    licence = "EUPL-1.2+"
+    copyright = (
+        ([2017, 2018, 2019, 2020], "Jonathan Weth", "wethjo@katharineum.de"),
+        ([2017, 2018, 2019], "Frank Poetzsch-Heffter", "p-h@katharineum.de"),
+        ([2018, 2019, 2020], "Hangzhi Yu", "yuha@katharineum.de"),
+        ([2018, 2019, 2020], "Julian Leucker", "leuckeju@katharineum.de"),
+        ([2019, 2020], "Dominik George", "dominik.george@teckids.org"),
+        ([2019, 2020], "mirabilos", "thorsten.glaser@teckids.org"),
+        ([2019, 2020], "Tom Teichler", "tom.teichler@teckids.org"),
+    )
+
     def config_updated(self, *args, **kwargs) -> None:
         clean_scss()
 
diff --git a/aleksis/core/settings.py b/aleksis/core/settings.py
index eb4b3e07401fae2a830c530212747877ced2af93..81c00f57599429b58494b7e38fb5a6f5651fdbb1 100644
--- a/aleksis/core/settings.py
+++ b/aleksis/core/settings.py
@@ -1,7 +1,9 @@
 import os
 import sys
 from glob import glob
+from importlib import import_module
 
+from django.apps import apps
 from django.utils.translation import gettext_lazy as _
 from calendarweek.django import i18n_day_name_choices_lazy
 
diff --git a/aleksis/core/templates/core/about.html b/aleksis/core/templates/core/about.html
new file mode 100644
index 0000000000000000000000000000000000000000..5b9097f8e294b099151d90dc91fbb583bd9e6dc9
--- /dev/null
+++ b/aleksis/core/templates/core/about.html
@@ -0,0 +1,122 @@
+{# -*- engine:django -*- #}
+{% extends "core/base.html" %}
+{% load i18n %}
+
+
+{% block browser_title %}{% blocktrans %}About AlekSIS{% endblocktrans %}{% endblock %}
+{% block page_title %}{% blocktrans %}AlekSIS – The Free School Information System{% endblocktrans %}{% endblock %}
+
+{% block content %}
+
+  <div class="row">
+    <div class="col s12">
+      <div class="card">
+        <div class="card-content">
+          <span class="card-title">{% blocktrans %}About AlekSIS{% endblocktrans %}</span>
+          <p>
+            {% blocktrans %}
+              This platform is powered by AlekSIS, a web-based school information system (SIS) which can be used
+              to manage and/or publish organisational subjects of educational institutions. AlekSIS is free software and
+              can be used by everyone.
+            {% endblocktrans %}
+          </p>
+        </div>
+        <div class="card-action">
+          <a class="" href="https://aleksis.org/">{% trans "Website of AlekSIS" %}</a>
+          <a class="" href="https://edugit.org/AlekSIS/">{% trans "Source code" %}</a>
+        </div>
+      </div>
+    </div>
+  </div>
+  <div class="row">
+    <div class="col s12">
+      <div class="card">
+        <div class="card-content">
+          <span class="card-title">{% trans "Licence information" %}</span>
+          <p>
+            {% blocktrans %}
+              The core and the official apps of AlekSIS are licenced under the EUPL, version 1.2 or later. For licence
+              information from third-party apps, if installed, see directly at the respective components below. The
+              licences are marked like this:
+            {% endblocktrans %}
+          </p>
+          <br/>
+          <p>
+            <span class="chip green white-text">{% trans "Free/Open Source Licence" %}</span>
+            <span class="chip orange white-text">{% trans "Other Licence" %}</span>
+          </p>
+        </div>
+        <div class="card-action">
+          <a href="https://eupl.eu">{% trans "Full licence text" %}</a>
+          <a href="https://joinup.ec.europa.eu/collection/eupl/guidelines-users-and-developers">{% trans "More information about the EUPL" %}</a>
+        </div>
+      </div>
+    </div>
+  </div>
+
+  <div class="row">
+    {% for app_config in app_configs %}
+      <div class="col s12 m12 l6">
+        <div class="card " id="{{ app_config.name }}">
+          <div class="card-content">
+            {% if app_config.get_licence.1.isFsfLibre %}
+              <span class="chip green white-text right">Free Software</span>
+            {% elif app_config.get_licence.1.isOsiApproved %}
+              <span class="chip green white-text right">Open Source</span>
+            {% endif %}
+
+            <span class="card-title">{{ app_config.get_name }} <small>{{ app_config.get_version }}</small></span>
+
+            {% if app_config.get_copyright %}
+              <p>
+                {% for holder in app_config.get_copyright %}
+                  Copyright © {{ holder.0 }}
+
+                  {% if holder.2 %}
+                    <a href="mailto:{{ holder.2 }}">{{ holder.1 }}</a>
+                  {% else %}
+                    {{ holder.1 }}
+                  {% endif %}
+
+                  <br/>
+                {% endfor %}
+              </p>
+              <br/>
+            {% endif %}
+
+            {% if app_config.get_licence %}
+              {% with licence=app_config.get_licence %}
+                <p>
+                  {% blocktrans with licence=licence.0 %}
+                    This app is licenced under {{ licence }}.
+                  {% endblocktrans %}
+                </p>
+                <br/>
+                <p>
+                  {% for l in licence.2 %}
+                    <a class="chip white-text {% if l.isOsiApproved or l.isFsfLibre %}green{% else %}orange{% endif %}"
+                       href="{{ l.url }}">
+                      {{ l.name }}
+                    </a>
+                  {% endfor %}
+                </p>
+              {% endwith %}
+            {% endif %}
+          </div>
+          {% if app_config.get_urls %}
+            <div class="card-action">
+              {% for url_name, url in app_config.get_urls.items %}
+                <a href="{{ url }}">{{ url_name }}</a>
+              {% endfor %}
+            </div>
+          {% endif %}
+        </div>
+      </div>
+      {% if forloop.counter|divisibleby:2 %}
+        </div>
+        <div class="row">
+      {% endif %}
+    {% endfor %}
+    </div>
+
+{% endblock %}
diff --git a/aleksis/core/templates/core/base.html b/aleksis/core/templates/core/base.html
index 8ac5ed74a195be76a558ae30e780d493ae6c2ecf..fe536557737d05a04c93d9bfbedfcc63f1bbe60b 100644
--- a/aleksis/core/templates/core/base.html
+++ b/aleksis/core/templates/core/base.html
@@ -146,8 +146,8 @@
   <div class="footer-copyright">
     <div class="container">
       <div class="left">
-        <a class="blue-text text-lighten-4" href="https://aleksis.org/">
-          AlekSIS — The Free School Information System
+        <a class="blue-text text-lighten-4" href="{% url "about_aleksis" %}">
+          {% trans "About AlekSIS — The Free School Information System" %}
         </a>
         © The AlekSIS Team
       </div>
diff --git a/aleksis/core/urls.py b/aleksis/core/urls.py
index 4bb7d10fbbaa6774d5f5beed3a1d7e034f020260..5de76472c91da9243edfa13c57c5ce29a7782e08 100644
--- a/aleksis/core/urls.py
+++ b/aleksis/core/urls.py
@@ -16,6 +16,7 @@ from . import views
 urlpatterns = [
     path("", include("pwa.urls"), name="pwa"),
     path("offline/", views.offline, name="offline"),
+    path("about/", views.about, name="about_aleksis"),
     path("admin/", admin.site.urls),
     path("data_management/", views.data_management, name="data_management"),
     path("status/", views.system_status, name="system_status"),
diff --git a/aleksis/core/util/apps.py b/aleksis/core/util/apps.py
index 7eef538ae05265d3492bda331eaf75875229a4ae..dbf6159252c0b20aee9fcc422fb21360c7fbc1e0 100644
--- a/aleksis/core/util/apps.py
+++ b/aleksis/core/util/apps.py
@@ -1,5 +1,5 @@
 from importlib import import_module
-from typing import Any, List, Optional, Tuple
+from typing import Any, List, Optional, Tuple, Sequence
 
 import django.apps
 from django.contrib.auth.signals import user_logged_in, user_logged_out
@@ -7,6 +7,10 @@ from django.db.models.signals import post_migrate, pre_migrate
 from django.http import HttpRequest
 
 from constance.signals import config_updated
+from license_expression import Licensing, LicenseSymbol
+from spdx_license_list import LICENSES
+
+from .core_helpers import copyright_years
 
 
 class AppConfig(django.apps.AppConfig):
@@ -41,6 +45,87 @@ class AppConfig(django.apps.AppConfig):
             # ImportErrors are non-fatal because checks are optional.
             pass
 
+    @classmethod
+    def get_name(cls):
+        return getattr(cls, "verbose_name", cls.name)
+        # TODO Try getting from distribution if not set
+
+    @classmethod
+    def get_version(cls):
+        try:
+            from .. import __version__  # noqa
+        except ImportError:
+            __version__ = None
+
+        return getattr(cls, "version", __version__)
+
+    @classmethod
+    def get_licence(cls) -> Tuple:
+        licence = getattr(cls, "licence", None)
+
+        default_dict = {
+            'isDeprecatedLicenseId': False,
+            'isFsfLibre': False,
+            'isOsiApproved': False,
+            'licenseId': 'unknown',
+            'name': 'Unknown Licence',
+            'referenceNumber': -1,
+            'url': '',
+        }
+
+        if licence:
+            licensing = Licensing(LICENSES.keys())
+            parsed = licensing.parse(licence).simplify()
+            readable = parsed.render_as_readable()
+
+            flags = {
+                "isFsfLibre": True,
+                "isOsiApproved": True,
+            }
+
+            licence_dicts = []
+
+            for symbol in parsed.symbols:
+                licence_dict = LICENSES.get(symbol.key.rstrip("+"), None)
+
+                if licence_dict is None:
+                    licence_dict = default_dict
+                else:
+                    licence_dict["url"] = "https://spdx.org/licenses/{}.html".format(licence_dict["licenseId"])
+
+                flags["isFsfLibre"] = flags["isFsfLibre"] and licence_dict["isFsfLibre"]
+                flags["isOsiApproved"] = flags["isOsiApproved"] and licence_dict["isOsiApproved"]
+
+                licence_dicts.append(licence_dict)
+
+            return (readable, flags, licence_dicts)
+        else:
+            return ("Unknown", [default_dict])
+
+    @classmethod
+    def get_urls(cls):
+        return getattr(cls, "urls", {})
+        # TODO Try getting from distribution if not set
+
+    @classmethod
+    def get_copyright(cls) -> Sequence[Tuple[str, str, str]]:
+        copyrights = getattr(cls, "copyright", tuple())
+
+        copyrights_processed = []
+
+        for copyright in copyrights:
+            copyrights_processed.append(
+                (
+                    copyright[0] if isinstance(copyright[0], str) else copyright_years(copyright[0]),
+                    copyright[1],
+                    copyright[2],
+                )
+            )
+
+        return copyrights_processed
+
+        # TODO Try getting from distribution if not set
+
     def config_updated(
         self,
         key: Optional[str] = "",
diff --git a/aleksis/core/util/core_helpers.py b/aleksis/core/util/core_helpers.py
index 9f91154c30913ae14eff1332b5f6bea3207bfa86..13558233068626c8f288b70624b2646840826819 100644
--- a/aleksis/core/util/core_helpers.py
+++ b/aleksis/core/util/core_helpers.py
@@ -1,8 +1,10 @@
 from datetime import datetime, timedelta
+from itertools import groupby
+from operator import itemgetter
 import os
 import pkgutil
 from importlib import import_module
-from typing import Any, Callable, Sequence, Union
+from typing import Any, Callable, Sequence, Union, List
 from uuid import uuid4
 
 from django.conf import settings
@@ -12,6 +14,18 @@ from django.utils import timezone
 from django.utils.functional import lazy
 
 
+def copyright_years(years: Sequence[int], seperator: str = ", ", joiner: str = "–") -> str:
+    """ Takes a sequence of integegers and produces a string with ranges
+
+    >>> copyright_years([1999, 2000, 2001, 2005, 2007, 2008, 2009])
+    '1999–2001, 2005, 2007–2009'
+    """
+
+    ranges = [list(map(itemgetter(1), group)) for _, group in groupby(enumerate(years), lambda e: e[1]-e[0])]
+    years_strs = [str(range_[0]) if len(range_) == 1 else joiner.join([str(range_[0]), str(range_[-1])]) for range_ in ranges]
+
+    return seperator.join(years_strs)
+
 def dt_show_toolbar(request: HttpRequest) -> bool:
     from debug_toolbar.middleware import show_toolbar  # noqa
 
diff --git a/aleksis/core/views.py b/aleksis/core/views.py
index d3ead324d90b313ffeec3a5068df24c90bc379f4..51a89e5552158188973b2c244cfdce63b8604b02 100644
--- a/aleksis/core/views.py
+++ b/aleksis/core/views.py
@@ -1,5 +1,7 @@
+from importlib import import_module
 from typing import Optional
 
+from django.apps import apps
 from django.contrib.auth.decorators import login_required
 from django.core.exceptions import PermissionDenied
 from django.http import Http404, HttpRequest, HttpResponse
@@ -22,6 +24,7 @@ from .forms import (
 from .models import Activity, Group, Notification, Person, School, DashboardWidget, Announcement
 from .tables import GroupsTable, PersonsTable
 from .util import messages
+from .util.apps import AppConfig
 
 
 @person_required
@@ -52,6 +55,14 @@ def offline(request):
     return render(request, "core/offline.html")
 
 
+def about(request):
+    context = {}
+
+    context["app_configs"] = list(filter(lambda a: isinstance(a, AppConfig), apps.get_app_configs()))
+
+    return render(request, "core/about.html", context)
+
+
 @login_required
 def persons(request: HttpRequest) -> HttpResponse:
     context = {}
diff --git a/pyproject.toml b/pyproject.toml
index 02448d52ad00c58009c4ba7cb4244f2a3c7da808..dcc6ed027e169c417be226917d2f77955b461999 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -71,6 +71,8 @@ django-memoize = "^2.2.1"
 django-haystack = "^3.0"
 celery-haystack = {version="^0.3.1", optional=true}
 django-dbbackup = "^3.3.0"
+spdx-license-list = "^0.4.0"
+license-expression = "^1.2"
 
 [tool.poetry.extras]
 ldap = ["django-auth-ldap"]