diff --git a/software/theia/buildout.hash.cfg b/software/theia/buildout.hash.cfg
index 7c7af166774f8f00923cc6185d5722603967f18b..c91342c360c9a6cd76a282cc39b5d9ef74af1ba6 100644
--- a/software/theia/buildout.hash.cfg
+++ b/software/theia/buildout.hash.cfg
@@ -15,7 +15,7 @@
 
 [instance]
 filename = instance.cfg.in
-md5sum = 7c9444fbe8dc8faea67ede2b77e188ed
+md5sum = 21735765808aac82fb91d53341a3c0d6
 
 [yarn.lock]
 filename = yarn.lock
diff --git a/software/theia/instance.cfg.in b/software/theia/instance.cfg.in
index a9cb2f6b493018bbce150d6c0578b581ab0e7835..9afd4845d2bf636a4f82bd86c4d7dbb60b686649 100644
--- a/software/theia/instance.cfg.in
+++ b/software/theia/instance.cfg.in
@@ -17,13 +17,31 @@ recipe = slapos.cookbook:generate.password
 username = node
 bytes = 12
 
+[frontend-instance-certificate]
+recipe = plone.recipe.command
+command =
+  if [ ! -e $${:key-file} ]
+  then
+    ${openssl-output:openssl} req -x509 -nodes -days 3650 \
+      -subj "/C=AA/ST=X/L=X/O=Dis/CN=$${:common-name}" \
+      -newkey rsa:1024 -keyout $${:key-file} \
+      -out $${:cert-file}
+  fi
+update-command = $${:command}
+key-file = $${directory:etc}/$${:_buildout_section_name_}.key
+cert-file = $${directory:etc}/$${:_buildout_section_name_}.crt
+common-name = $${frontend-instance-config:ip}
+location =
+  $${:key-file}
+  $${:cert-file}
+
 [frontend-instance-config]
 recipe = slapos.recipe.template:jinja2
 rendered = $${directory:etc}/$${:_buildout_section_name_}
 template = inline:
-  https://$${:hostname}:$${:port} {
+  :$${:port} {
     bind $${:ip}
-    tls self_signed # TODO
+    tls $${frontend-instance-certificate:cert-file} $${frontend-instance-certificate:key-file}
     log stdout
     errors stderr
     gzip
@@ -57,7 +75,7 @@ ip = $${frontend-instance-config:ip}
 hostname = $${frontend-instance-config:hostname}
 port = $${frontend-instance-config:port}
 pidfile = $${directory:pidfiles}/$${:_buildout_section_name_}.pid
-url = https://$${frontend-instance-password:username}:$${frontend-instance-password:passwd}@$${:hostname}:$${:port}/
+url = https://$${:hostname}:$${:port}/
 
 [frontend-reload]
 recipe = slapos.cookbook:wrapper
@@ -108,10 +126,24 @@ name = $${:_buildout_section_name_}.py
 config-hostname = $${frontend-instance:ip}
 config-port = $${frontend-instance:port}
 
+[apache-frontend]
+<= slap-connection
+recipe = slapos.cookbook:requestoptional
+name = Theia Frontend
+# XXX We have hardcoded SR URL here.
+software-url = http://git.erp5.org/gitweb/slapos.git/blob_plain/HEAD:/software/apache-frontend/software.cfg
+slave = true
+config-url = $${frontend-instance:url}
+config-https-only = true
+config-type = websocket
+config-websocket-path-list = /services
+return = domain secure_access
+
 [publish-connection-parameter]
 recipe = slapos.cookbook:publish
-url = $${frontend-instance:url}
-
+url = $${apache-frontend:connection-secure_access}
+username = $${frontend-instance-password:username}
+password = $${frontend-instance-password:passwd}
 
 [instance-parameter]
 recipe = slapos.cookbook:slapconfiguration
diff --git a/software/theia/test/test.py b/software/theia/test/test.py
index afb2690d25a79b6e2009fd0f04bdd7cd3d42d761..b488951d55dc92f25cc4124c7bed6e445fac678e 100644
--- a/software/theia/test/test.py
+++ b/software/theia/test/test.py
@@ -44,19 +44,20 @@ setUpModule, SlapOSInstanceTestCase = makeModuleSetUpAndTestCaseClass(
 
 class TestTheia(SlapOSInstanceTestCase):
   def setUp(self):
-    self.theia_url = self.computer_partition.getConnectionParameterDict(
-    )['url']
+    self.connection_parameters = self.computer_partition.getConnectionParameterDict()
 
   def test_http_get(self):
-    resp = requests.get(self.theia_url, verify=False)
-    self.assertEqual(requests.codes.ok, resp.status_code)
+    resp = requests.get(self.connection_parameters['url'], verify=False)
+    self.assertEqual(requests.codes.unauthorized, resp.status_code)
 
-    # without login/password, this is unauthorized
-    parsed_url = urlparse(self.theia_url)
+    # with login/password, this is allowed
+    parsed_url = urlparse(self.connection_parameters['url'])
     resp = requests.get(
         parsed_url._replace(
-            netloc='[{}]:{}'.format(
+            netloc='{}:{}@[{}]:{}'.format(
+                self.connection_parameters['username'],
+                self.connection_parameters['password'],
                 parsed_url.hostname,
                 parsed_url.port)).geturl(),
         verify=False)
-    self.assertEqual(requests.codes.unauthorized, resp.status_code)
+    self.assertEqual(requests.codes.ok, resp.status_code)