diff --git a/vospace-ui-backend/pom.xml b/vospace-ui-backend/pom.xml
index f79424232d75ac72192efcb820df92adc35b543c..7fc3e0739aed1425547f6649dad30c9d53e3675c 100644
--- a/vospace-ui-backend/pom.xml
+++ b/vospace-ui-backend/pom.xml
@@ -106,6 +106,9 @@
             <plugin>
                 <artifactId>maven-surefire-plugin</artifactId>
                 <version>2.22.2</version>
+                <configuration>
+                    <trimStackTrace>false</trimStackTrace>
+                </configuration>
             </plugin>
             <plugin>
                 <groupId>org.jacoco</groupId>
diff --git a/vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/client/VOSpaceClient.java b/vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/client/VOSpaceClient.java
index 819a285d6581ecc29b9433edc6223ee5a7ae24d4..c4a3659c492987f2e44d702f561f1fb2ecea9883 100644
--- a/vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/client/VOSpaceClient.java
+++ b/vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/client/VOSpaceClient.java
@@ -142,7 +142,7 @@ public class VOSpaceClient {
 
         String path = node.getUri().substring(("vos://" + authority).length());
 
-        HttpRequest request = getRequest("/nodes" + urlEncodePath(path))
+        HttpRequest request = getRequest("/nodes" + path)
                 .header("Accept", useJson ? "application/json" : "text/xml")
                 .header("Content-Type", useJson ? "application/json" : "text/xml")
                 .PUT(HttpRequest.BodyPublishers.ofString(marshal(node)))
@@ -166,7 +166,7 @@ public class VOSpaceClient {
 
         String path = node.getUri().substring(("vos://" + authority).length());
 
-        HttpRequest request = getRequest("/nodes" + urlEncodePath(path) + "?recursive=" + recursive)
+        HttpRequest request = getRequest("/nodes" + path + "?recursive=" + recursive)
                 .header("Accept", useJson ? "application/json" : "text/xml")
                 .header("Content-Type", useJson ? "application/json" : "text/xml")
                 .POST(HttpRequest.BodyPublishers.ofString(marshal(node)))
diff --git a/vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/controller/NodesController.java b/vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/controller/NodesController.java
index fe5ffa615e2ee1ed784bad177b0a3b31e736a97e..5413860675d77eeceb1690e72448c0f0f51b8637 100644
--- a/vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/controller/NodesController.java
+++ b/vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/controller/NodesController.java
@@ -13,6 +13,7 @@ import it.inaf.ia2.vospace.ui.exception.VOSpaceException;
 import it.inaf.ia2.vospace.ui.service.MainNodesHtmlGenerator;
 import it.inaf.ia2.vospace.ui.service.MoveNodeModalHtmlGenerator;
 import it.inaf.oats.vospace.datamodel.NodeUtils;
+import static it.inaf.oats.vospace.datamodel.NodeUtils.urlEncodePath;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
@@ -79,6 +80,8 @@ public class NodesController extends BaseController {
     @GetMapping(value = "/nodesForMove", produces = MediaType.APPLICATION_JSON_VALUE)
     public ResponseEntity<ListNodeData> listNodesForMoveModal(@RequestParam("path") String path, @RequestParam("nodeToMove") String nodeToMove, User principal) throws Exception {
 
+        LOG.debug("listNodes called for path {}", path);
+        
         ListNodeData listNodeData = new ListNodeData();
 
         Node node = client.getNode(path);
@@ -99,7 +102,7 @@ public class NodesController extends BaseController {
 
         Transfer transfer = new Transfer();
         transfer.setDirection("pullFromVoSpace");
-        transfer.setTarget(Arrays.asList("vos://" + authority + path));
+        transfer.setTarget(Arrays.asList("vos://" + authority + urlEncodePath(path)));
 
         Protocol protocol = new Protocol();
         protocol.setUri("ivo://ivoa.net/vospace/core#httpget");
@@ -123,7 +126,7 @@ public class NodesController extends BaseController {
         LOG.debug("newFolder called for path {}/{}", parentPath, name);
 
         ContainerNode node = new ContainerNode();
-        node.setUri("vos://" + authority + parentPath + "/" + name);
+        node.setUri("vos://" + authority + urlEncodePath(parentPath + "/" + name));
 
         Property creator = new Property();
         creator.setUri("ivo://ivoa.net/vospace/core#creator");
@@ -162,8 +165,8 @@ public class NodesController extends BaseController {
     @PostMapping(value = "/move")
     public ResponseEntity<Job> moveNode(@RequestBody Map<String, Object> params) {
 
-        String target = getRequiredParam(params, "target");
-        String direction = getRequiredParam(params, "direction");
+        String target = urlEncodePath(getRequiredParam(params, "target"));
+        String direction = urlEncodePath(getRequiredParam(params, "direction"));
 
         Transfer transfer = new Transfer();
         transfer.setTarget(Arrays.asList("vos://" + authority + target));
diff --git a/vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/controller/UploadController.java b/vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/controller/UploadController.java
index 3508f3f9d598ac85b36ff105bd767d2ee3e935c9..a77fc8202f9bff8fe70a126b5ec71ea3e8e1f975 100644
--- a/vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/controller/UploadController.java
+++ b/vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/controller/UploadController.java
@@ -8,6 +8,7 @@ package it.inaf.ia2.vospace.ui.controller;
 import it.inaf.ia2.vospace.ui.client.VOSpaceClient;
 import it.inaf.ia2.vospace.ui.data.UploadFilesData;
 import it.inaf.ia2.vospace.ui.exception.PermissionDeniedException;
+import static it.inaf.oats.vospace.datamodel.NodeUtils.urlEncodePath;
 import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.CompletableFuture;
@@ -64,11 +65,15 @@ public class UploadController extends BaseController {
     public CompletableFuture<String> prepareForDownload(String parentPath, String fileName) {
 
         return CompletableFuture.supplyAsync(() -> {
-            String nodeUri = "vos://" + authority + parentPath;
-            if (!nodeUri.endsWith("/")) {
-                nodeUri += "/";
+
+            String path = parentPath;
+
+            if (!path.endsWith("/")) {
+                path += "/";
             }
-            nodeUri += fileName;
+            path += fileName;
+
+            String nodeUri = "vos://" + authority + urlEncodePath(path);
 
             createDataNode(nodeUri, getUser().getName());
 
diff --git a/vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/service/NodeInfo.java b/vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/service/NodeInfo.java
index 68e51080d63d0b0a588dd6d216fe56516a28cdf6..5b3d6154514a8f75bc51aecaf5b947be116762a3 100644
--- a/vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/service/NodeInfo.java
+++ b/vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/service/NodeInfo.java
@@ -9,12 +9,14 @@ import it.inaf.ia2.aa.data.User;
 import it.inaf.ia2.vospace.ui.exception.VOSpaceException;
 import it.inaf.oats.vospace.datamodel.NodeProperties;
 import it.inaf.oats.vospace.datamodel.NodeUtils;
-import java.util.List;
+import java.net.URLDecoder;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
 import java.util.Optional;
+import java.util.stream.Collectors;
 import net.ivoa.xml.vospace.v2.DataNode;
 import net.ivoa.xml.vospace.v2.Node;
 import net.ivoa.xml.vospace.v2.Property;
-import net.ivoa.xml.vospace.v2.View;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -41,7 +43,7 @@ public class NodeInfo {
     public NodeInfo(Node node, User user, String authority) {
         this.authority = authority;
         this.path = getPath(node);
-        this.name = path.substring(path.lastIndexOf("/") + 1);
+        this.name = URLDecoder.decode(path.substring(path.lastIndexOf("/") + 1), StandardCharsets.UTF_8);
         this.size = getSize(node);
         this.type = node.getType();
         this.creator = getCreator(node);
@@ -65,7 +67,10 @@ public class NodeInfo {
             throw new VOSpaceException("Node authority is different from configured one! Configured is " + authority + ", but node URI is " + uri);
         }
 
-        return uri.substring(prefix.length());
+        // returns decoded path
+        return String.join("/", Arrays.stream(uri.substring(prefix.length()).split("/"))
+                .map(p -> URLDecoder.decode(p, StandardCharsets.UTF_8))
+                .collect(Collectors.toList()));
     }
 
     private String getCreator(Node node) {
diff --git a/vospace-ui-backend/src/test/java/it/inaf/ia2/vospace/ui/client/VOSpaceClientTest.java b/vospace-ui-backend/src/test/java/it/inaf/ia2/vospace/ui/client/VOSpaceClientTest.java
index a816220c99f38691df72680e8f7f62855a502d59..2fa498dfd106d8560f012153d44cf52dbc9e48f7 100644
--- a/vospace-ui-backend/src/test/java/it/inaf/ia2/vospace/ui/client/VOSpaceClientTest.java
+++ b/vospace-ui-backend/src/test/java/it/inaf/ia2/vospace/ui/client/VOSpaceClientTest.java
@@ -10,6 +10,7 @@ import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.UncheckedIOException;
+import java.net.URISyntaxException;
 import java.net.http.HttpClient;
 import java.net.http.HttpHeaders;
 import java.net.http.HttpResponse;
@@ -24,6 +25,8 @@ import net.ivoa.xml.vospace.v2.Protocol;
 import net.ivoa.xml.vospace.v2.Transfer;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
@@ -90,17 +93,17 @@ public class VOSpaceClientTest {
     }
 
     @Test
-    public void testCreateNodeBadName() {
+    public void testCreateNodeBadUri() {
 
         ContainerNode newNode = new ContainerNode();
-        newNode.setUri("vos://ia2.inaf.it!vospace/mynode/File with spaces.and.dots.pdf");
+        newNode.setUri("vos://ia2.inaf.it!vospace/mynode/spaces not encoded");
 
-        ReflectionTestUtils.setField(voSpaceClient, "useJson", false);
-
-        CompletableFuture response = getMockedStreamResponseFuture(200, getResourceFileContent("node-response.xml"));
-        when(mockedHttpClient.sendAsync(any(), any())).thenReturn(response);
-
-        voSpaceClient.createNode(newNode);
+        try {
+            voSpaceClient.createNode(newNode);
+            fail("Exception was expected");
+        } catch (IllegalArgumentException ex) {
+            assertTrue(ex.getCause() instanceof URISyntaxException);
+        }
     }
 
     @Test
@@ -195,6 +198,41 @@ public class VOSpaceClientTest {
 
         assertEquals(ExecutionPhase.COMPLETED, voSpaceClient.getJobPhase("job_id"));
     }
+    
+    @Test
+    public void testSetNode() {
+
+        ContainerNode node = new ContainerNode();
+        node.setUri("vos://ia2.inaf.it!vospace/mynode");
+
+        CompletableFuture response = getMockedStreamResponseFuture(200, getResourceFileContent("node-response.xml"));
+        when(mockedHttpClient.sendAsync(any(), any())).thenReturn(response);
+
+        voSpaceClient.setNode(node, true);
+    }
+    
+    @Test
+    public void testSetNodeBadUri() {
+
+        ContainerNode node = new ContainerNode();
+        node.setUri("vos://ia2.inaf.it!vospace/not urlencoded");
+
+        try {
+            voSpaceClient.setNode(node, true);
+            fail("Exception was expected");
+        } catch (IllegalArgumentException ex) {
+            assertTrue(ex.getCause() instanceof URISyntaxException);
+        }
+    }
+ 
+    @Test
+    public void testDeleteNode() {
+
+        CompletableFuture response = getMockedStreamResponseFuture(200, "");
+        when(mockedHttpClient.sendAsync(any(), any())).thenReturn(response);
+
+        voSpaceClient.deleteNode("/not urlencoded");
+    }
 
     protected static String getResourceFileContent(String fileName) {
         try ( InputStream in = VOSpaceClientTest.class.getClassLoader().getResourceAsStream(fileName)) {
diff --git a/vospace-ui-backend/src/test/java/it/inaf/ia2/vospace/ui/controller/NodesControllerTest.java b/vospace-ui-backend/src/test/java/it/inaf/ia2/vospace/ui/controller/NodesControllerTest.java
index 5879848a56e21417273b28c52012903d74f2526c..013590ded3a774ae6ade34d97ff1db56f3e495b8 100644
--- a/vospace-ui-backend/src/test/java/it/inaf/ia2/vospace/ui/controller/NodesControllerTest.java
+++ b/vospace-ui-backend/src/test/java/it/inaf/ia2/vospace/ui/controller/NodesControllerTest.java
@@ -6,12 +6,14 @@
 package it.inaf.ia2.vospace.ui.controller;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
+import it.inaf.ia2.aa.data.User;
 import it.inaf.ia2.vospace.ui.client.VOSpaceClient;
 import it.inaf.ia2.vospace.ui.data.Job;
 import it.inaf.ia2.vospace.ui.exception.VOSpaceException;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
+import javax.servlet.http.HttpSession;
 import net.ivoa.xml.uws.v1.ExecutionPhase;
 import net.ivoa.xml.uws.v1.JobSummary;
 import net.ivoa.xml.vospace.v2.ContainerNode;
@@ -24,6 +26,7 @@ import org.junit.jupiter.api.Test;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -33,6 +36,7 @@ import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.boot.test.mock.mockito.MockBean;
 import org.springframework.http.MediaType;
 import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.util.ReflectionTestUtils;
 import org.springframework.test.web.servlet.MockMvc;
 import org.springframework.test.web.servlet.ResultActions;
 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
@@ -50,6 +54,9 @@ public class NodesControllerTest {
     @MockBean
     private VOSpaceClient client;
 
+    @Autowired
+    private NodesController nodesController;
+    
     @Autowired
     private MockMvc mockMvc;
 
@@ -198,6 +205,33 @@ public class NodesControllerTest {
         }
     }
 
+    @Test
+    public void testDirectDownload() throws Exception {
+
+        when(client.getFileServiceEndpoint(any())).thenReturn("http://redirect");
+
+        mockMvc.perform(get("/download/myfile"))
+                .andExpect(status().is3xxRedirection());
+
+        verify(client, times(1)).getFileServiceEndpoint(any());
+    }
+
+    @Test
+    public void testNewFolder() throws Exception {
+
+        HttpSession mockedSession = mock(HttpSession.class);
+        ReflectionTestUtils.setField(nodesController, "session", mockedSession);
+
+        when(mockedSession.getAttribute("user_data")).thenReturn(mock(User.class));
+
+        mockMvc.perform(post("/folder")
+                .contentType(MediaType.APPLICATION_JSON)
+                .content("{\"parentPath\": \"/parent\", \"name\": \"newFolder\"}"))
+                .andExpect(status().is2xxSuccessful());
+
+        verify(client, times(1)).createNode(any());
+    }
+    
     private ResultActions testMoveNode() throws Exception {
 
         JobSummary job = new JobSummary();
diff --git a/vospace-ui-backend/src/test/java/it/inaf/ia2/vospace/ui/service/NodeInfoTest.java b/vospace-ui-backend/src/test/java/it/inaf/ia2/vospace/ui/service/NodeInfoTest.java
index 9ed64e0555aa3e657a97729068ad1b7511038aca..dacb50001f1da687d2e5f4722d7e31f83fa50723 100644
--- a/vospace-ui-backend/src/test/java/it/inaf/ia2/vospace/ui/service/NodeInfoTest.java
+++ b/vospace-ui-backend/src/test/java/it/inaf/ia2/vospace/ui/service/NodeInfoTest.java
@@ -50,6 +50,14 @@ public class NodeInfoTest {
     public void testSizePeta() {
         testNodeLength(6963696737150000L, "6.2 PB");
     }
+    
+    @Test
+    public void testUrlEncoding() {
+        DataNode node = new DataNode();
+        node.setUri("vos://example.com!vospace/my%23node");
+        NodeInfo nodeInfo = new NodeInfo(node, new User(), AUTHORITY);
+        assertEquals("my#node", nodeInfo.getName());
+    }
 
     private void testNodeLength(long bytes, String expectedText) {
         DataNode node = getDataNode();