diff --git a/services/webapp/code/rosetta/core_app/utils.py b/services/webapp/code/rosetta/core_app/utils.py
index afba4a7e7d79bd2de781e196e1796578a95638ee..a8b8e791bf4dd6b4262941b5f9978765ebb7c8f4 100644
--- a/services/webapp/code/rosetta/core_app/utils.py
+++ b/services/webapp/code/rosetta/core_app/utils.py
@@ -518,10 +518,10 @@ def hash_string_to_int(string):
 
 
 #================================
-#  Tunnel setup
+#  Tunnel (and proxy) setup
 #================================
 
-def setup_tunnel(task):
+def setup_tunnel_and_proxy(task):
 
     # Importing here instead of on top avoids circular dependencies problems when loading booleanize in settings
     from .models import Task, KeyPair, TaskStatuses
@@ -545,9 +545,108 @@ def setup_tunnel(task):
 
         task.tcp_tunnel_port = tcp_tunnel_port
         task.save()
+        
+    # Setup the proxy now.
+    # Some info about the various SSL switches: https://serverfault.com/questions/577616/using-https-between-apache-loadbalancer-and-backends
+
+    # Esnure conf directory exists
+    if not os.path.exists('/shared/etc_apache2_sites_enabled'):
+        os.makedirs('/shared/etc_apache2_sites_enabled')
+
+    # Set conf file name
+    apache_conf_file = '/shared/etc_apache2_sites_enabled/{}.conf'.format(task.uuid)
+
+    # Check if proxy conf exists 
+    if not os.path.exists(apache_conf_file):
+
+        # Write conf file
+        logger.debug('Writing task proxy conf to {}'.format(apache_conf_file))
+    
+        websocket_protocol = 'wss' if task.container.interface_protocol == 'https' else 'ws'
+        task_proxy_host = os.environ.get('TASK_PROXY_HOST', 'localhost')
+        apache_conf_content = '''
+#---------------------------
+#  Task interface proxy 
+#---------------------------
+
+#<Location /desktop/{0}/>
+#AuthType Basic
+#AuthName "Restricted area"
+#AuthUserFile /shared/reyns/etc_apache2_sites_enabled/'''+str(task.uuid)+'''.htpasswd
+#Require valid-user  
+
+#ProxyPass http://desktop-{0}:8590/
+#ProxyPassReverse http://desktop-{0}:8590/
+#</Location>     
+
+#<Location /sessions/{1}>
+#ProxyPass ws://desktop-{0}:8590/websockify
+#ProxyPassReverse ws://desktop-{0}:8590/websockify
+#</Location>
+
+Listen '''+str(task.tcp_tunnel_port)+'''
+<VirtualHost _default_:'''+str(task.tcp_tunnel_port)+'''>
+    
+    ServerName  '''+task_proxy_host+'''
+    ServerAdmin admin@rosetta
+    
+    SSLEngine on
+    SSLCertificateFile /root/certificates/rosetta_platform/rosetta_platform.crt
+    SSLCertificateKeyFile /root/certificates/rosetta_platform/rosetta_platform.key
+    SSLCACertificateFile /root/certificates/rosetta_platform/rosetta_platform.ca-bundle
+    
+    SSLProxyEngine On
+    SSLProxyVerify none 
+    SSLProxyCheckPeerCN off
+    SSLProxyCheckPeerName off  
+
+    BrowserMatch "MSIE [2-6]" \
+        nokeepalive ssl-unclean-shutdown \
+        downgrade-1.0 force-response-1.0
+    BrowserMatch "MSIE [17-9]" ssl-unclean-shutdown
+
+    # Use RewriteEngine to handle websocket connection upgrades
+    RewriteEngine On
+    RewriteCond %{HTTP:Connection} Upgrade [NC]
+    RewriteCond %{HTTP:Upgrade} websocket [NC]
+    RewriteRule /(.*) '''+str(websocket_protocol)+'''://webapp:'''+str(task.tcp_tunnel_port)+'''/$1 [P,L]
+
+    <Location "/">
+      # preserve Host header to avoid cross-origin problems
+      ProxyPreserveHost on
+      # proxy to the port
+      ProxyPass         '''+str(task.container.interface_protocol)+'''://webapp:'''+str(task.tcp_tunnel_port)+'''/
+      ProxyPassReverse  '''+str(task.container.interface_protocol)+'''://webapp:'''+str(task.tcp_tunnel_port)+'''/
+    </Location>
+
+</VirtualHost>
+
+'''
+        with open(apache_conf_file, 'w') as f:
+            f.write(apache_conf_content)
+    
+    # Now check conf exist on proxy
+    logger.debug('Checking if conf is enabled on proxy service')
+    out = os_shell('ssh -o StrictHostKeyChecking=no proxy "[ -e /etc/apache2/sites-enabled/{}.conf ]"'.format(task.uuid), capture=True)
+
+    if out.exit_code == 1:
+  
+        logger.debug('Conf not enabled on proxy service, linkig it and reloading Apache conf')
+  
+        # Link on proxy since conf does not exist
+        out = os_shell('ssh -o StrictHostKeyChecking=no proxy "sudo ln -s /shared/etc_apache2_sites_enabled/{0}.conf /etc/apache2/sites-enabled/{0}.conf"'.format(task.uuid), capture=True)
+        if out.exit_code != 0:
+            logger.error(out.stderr)
+            raise ErrorMessage('Somthing went wrong when activating the task proxy conf')        
+        
+        # Reload apache conf on Proxy
+        out = os_shell('ssh -o StrictHostKeyChecking=no proxy "sudo apache2ctl graceful"', capture=True)
+        if out.exit_code != 0:
+            logger.error(out.stderr) 
+            raise ErrorMessage('Somthing went wrong when loading the task proxy conf')        
 
 
-    # Check if the tunnel is active and if not create it
+    # Check if the tunnel is (still) active and if not create it
     logger.debug('Checking if task "{}" has a running tunnel'.format(task))
 
     out = os_shell('ps -ef | grep ":{}:{}:{}" | grep -v grep'.format(task.tcp_tunnel_port, task.interface_ip, task.interface_port), capture=True)
diff --git a/services/webapp/code/rosetta/core_app/views.py b/services/webapp/code/rosetta/core_app/views.py
index bc01e877bf0ee67fa510c6fb45808fdc40daa7a1..2dc05f1442c8b249bf6e28804de6cc58692ec094 100644
--- a/services/webapp/code/rosetta/core_app/views.py
+++ b/services/webapp/code/rosetta/core_app/views.py
@@ -10,7 +10,7 @@ from django.contrib.auth.models import User
 from django.shortcuts import redirect
 from django.db.models import Q
 from .models import Profile, LoginToken, Task, TaskStatuses, Container, Computing, KeyPair, ComputingSysConf, ComputingUserConf, Text
-from .utils import send_email, format_exception, timezonize, os_shell, booleanize, debug_param, get_tunnel_host, random_username, setup_tunnel, finalize_user_creation
+from .utils import send_email, format_exception, timezonize, os_shell, booleanize, debug_param, get_tunnel_host, random_username, setup_tunnel_and_proxy, finalize_user_creation
 from .decorators import public_view, private_view
 from .exceptions import ErrorMessage
 
@@ -341,7 +341,7 @@ def tasks(request):
             elif action=='connect':
                 
                 # First ensure that the tunnel is setu up
-                setup_tunnel(task)
+                setup_tunnel_and_proxy(task)
 
                 # Then, redirect to the task through the tunnel
                 tunnel_host = get_tunnel_host()
@@ -972,16 +972,25 @@ def direct_connection_handler(request, uuid):
         raise ErrorMessage('You do not have access to this task.')
 
     # First ensure that the tunnel is setu up
-    setup_tunnel(task)
+    setup_tunnel_and_proxy(task)
+    
+    # Set task proxy host
+    task_proxy_host = settings.TASK_PROXY_HOST
 
     # Then, redirect to the task through the tunnel
     tunnel_host = get_tunnel_host()
-    if task.requires_proxy_auth and task.auth_token:
-        user = request.user.email
-        password = task.auth_token
-        return redirect('{}://{}:{}@{}:{}'.format(task.container.interface_protocol, user, password, tunnel_host, task.tcp_tunnel_port))
+    if task.requires_proxy:
+        if task.requires_proxy_auth and task.auth_token:
+            user = request.user.email
+            password = task.auth_token
+            redirect_string = 'https://{}:{}@{}:{}'.format(user, password, task_proxy_host, task.tcp_tunnel_port)        
+        else:
+            redirect_string = 'https://{}:{}'.format(task_proxy_host, task.tcp_tunnel_port)       
     else:
-        return redirect('{}://{}:{}'.format(task.container.interface_protocol, tunnel_host,task.tcp_tunnel_port))
+        redirect_string = '{}://{}:{}'.format(task.container.interface_protocol, tunnel_host,task.tcp_tunnel_port)
+    
+    logger.debug('Task direct connect redirect: "{}"'.format(redirect_string))
+    return redirect(redirect_string)
         
     
 
@@ -999,7 +1008,7 @@ def sharable_link_handler(request, short_uuid):
         raise ErrorMessage('You do not have access to this task.')
 
     # First ensure that the tunnel is setu up
-    setup_tunnel(task)
+    setup_tunnel_and_proxy(task)
 
     # Then, redirect to the task through the tunnel
     tunnel_host = get_tunnel_host()
diff --git a/services/webapp/code/rosetta/settings.py b/services/webapp/code/rosetta/settings.py
index 436f9d8ed56adda20eeba27ca57f2c62a885f1a9..aaba4219f24cded117fd43e5dbd8bf5ae85c516a 100644
--- a/services/webapp/code/rosetta/settings.py
+++ b/services/webapp/code/rosetta/settings.py
@@ -230,6 +230,8 @@ LOCAL_USER_DATA_DIR = os.environ.get('LOCAL_USER_DATA_DIR', '/data')
 # Invitation code if any
 INVITATION_CODE = os.environ.get('INVITATION_CODE', None)
 
+# Task proxy host (WARNING: direct use of the env var in utils.py)
+TASK_PROXY_HOST = os.environ.get('TASK_PROXY_HOST', 'localhost')
 
 #===============================
 #  Auth