diff --git a/actions/apache b/actions/apache
new file mode 100755
index 000000000..32d59743a
--- /dev/null
+++ b/actions/apache
@@ -0,0 +1,101 @@
+#!/usr/bin/python3
+# -*- mode: python -*-
+#
+# This file is part of Plinth.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+
+"""
+Configuration helper for Apache web server.
+"""
+
+import argparse
+import subprocess
+
+from plinth import action_utils
+
+
+def parse_arguments():
+ """Return parsed command line arguments as dictionary"""
+ parser = argparse.ArgumentParser()
+ subparsers = parser.add_subparsers(dest='subcommand', help='Sub command')
+ subparsers.add_parser('setup', help='Setup for Apache')
+
+ subparsers.required = True
+ return parser.parse_args()
+
+
+def subcommand_setup(_):
+ """Setup Apache configuration."""
+ # Regenerate the snakeoil self-signed SSL certificate. This is so that
+ # FreedomBox images don't all have the same certificate.
+ subprocess.run(['make-ssl-cert', 'generate-default-snakeoil',
+ '--force-overwrite'], check=True)
+
+ with action_utils.WebserverChange() as webserver:
+ # set the prefork worker model
+ webserver.disable('mpm_event', kind='module')
+ webserver.disable('mpm_worker', kind='module')
+ webserver.enable('mpm_prefork', kind='module')
+
+ # enable miscellaneous modules.
+ webserver.enable('proxy', kind='module')
+ webserver.enable('proxy_http', kind='module')
+ webserver.enable('rewrite', kind='module')
+
+ # enable GnuTLS
+ webserver.disable('ssl', kind='module')
+ webserver.enable('gnutls', kind='module')
+
+ # enable mod_alias for RedirectMatch
+ webserver.enable('alias', kind='module')
+
+ # enable mod_headers for HSTS
+ webserver.enable('headers', kind='module')
+
+ # enable some critical modules to avoid restart while installing Plinth
+ # applications.
+ webserver.enable('php7.0', kind='module')
+ webserver.enable('cgi', kind='module')
+ webserver.enable('authnz_ldap', kind='module')
+
+ # enable users to share files uploaded to ~/public_html
+ webserver.enable('userdir', kind='module')
+
+ # setup freedombox site
+ webserver.enable('freedombox', kind='config')
+
+ # enable serving Debian javascript libraries
+ webserver.enable('javascript-common', kind='config')
+
+ # default sites
+ webserver.enable('000-default', kind='site')
+ webserver.disable('default-ssl', kind='site')
+ webserver.enable('default-tls', kind='site')
+ webserver.enable('plinth', kind='site')
+ webserver.enable('plinth-ssl', kind='site')
+
+
+def main():
+ """Parse arguments and perform all duties"""
+ arguments = parse_arguments()
+
+ subcommand = arguments.subcommand.replace('-', '_')
+ subcommand_method = globals()['subcommand_' + subcommand]
+ subcommand_method(arguments)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/data/etc/plinth/modules-enabled/apache b/data/etc/plinth/modules-enabled/apache
new file mode 100644
index 000000000..b237f3e9d
--- /dev/null
+++ b/data/etc/plinth/modules-enabled/apache
@@ -0,0 +1 @@
+plinth.modules.apache
diff --git a/data/usr/lib/freedombox/setup.d/86_plinth b/data/usr/lib/freedombox/setup.d/86_plinth
index 3d88355fa..5ba1ccde6 100755
--- a/data/usr/lib/freedombox/setup.d/86_plinth
+++ b/data/usr/lib/freedombox/setup.d/86_plinth
@@ -18,21 +18,5 @@
set -e
-# Enable Apache modules required for Plinth.
-
-echo "Configuring Apache for Plinth..."
-
-make-ssl-cert generate-default-snakeoil
-a2enmod headers
-a2enmod proxy
-a2enmod proxy_http
-a2enmod rewrite
-a2enmod ssl
-a2enconf javascript-common
-a2ensite plinth.conf
-a2ensite plinth-ssl.conf
-
-echo "Done configuring Apache for Plinth."
-
# Ensure that DB and log file permissions are correct
chown -R plinth: /var/lib/plinth /var/log/plinth
diff --git a/plinth/modules/apache/__init__.py b/plinth/modules/apache/__init__.py
new file mode 100644
index 000000000..0b99a1955
--- /dev/null
+++ b/plinth/modules/apache/__init__.py
@@ -0,0 +1,34 @@
+#
+# This file is part of Plinth.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+
+"""
+Plinth module for Apache server.
+"""
+
+from plinth import actions
+
+version = 1
+
+is_essential = True
+
+managed_packages = ['apache2', 'libapache2-mod-gnutls', 'libapache2-mod-php']
+
+
+def setup(helper, old_version=False):
+ """Configure the module."""
+ helper.install(managed_packages)
+ actions.superuser_run('apache', ['setup'])
diff --git a/plinth/modules/apache/tests/__init__.py b/plinth/modules/apache/tests/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/plinth/modules/apache/urls.py b/plinth/modules/apache/urls.py
new file mode 100644
index 000000000..962181734
--- /dev/null
+++ b/plinth/modules/apache/urls.py
@@ -0,0 +1,23 @@
+#
+# This file is part of Plinth.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+
+"""
+URLs for the Apache module.
+"""
+
+urlpatterns = [
+]