diff --git a/src/main/java/it/inaf/oats/vospace/FileServiceClient.java b/src/main/java/it/inaf/oats/vospace/FileServiceClient.java
index f23a4b293c6496f8802ae2c017af658fa00f3fbe..574069be60909eec67c4dfa0bae5021a92bf8eab 100644
--- a/src/main/java/it/inaf/oats/vospace/FileServiceClient.java
+++ b/src/main/java/it/inaf/oats/vospace/FileServiceClient.java
@@ -10,6 +10,7 @@ import it.inaf.ia2.aa.data.User;
 import it.inaf.oats.vospace.datamodel.Views;
 import it.inaf.oats.vospace.exception.InvalidArgumentException;
 import java.io.OutputStream;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.stream.Collectors;
 import javax.servlet.http.HttpServletRequest;
@@ -38,6 +39,9 @@ public class FileServiceClient {
 
     @Autowired
     private HttpServletRequest request;
+    
+    @Autowired
+    private LinkService linkService;
 
     public String startArchiveJob(Transfer transfer, String jobId) {
 
@@ -63,6 +67,9 @@ public class FileServiceClient {
             // Add target path
             vosPaths.add(target);
         }
+        
+        // follow links to links in vosPaths
+        vosPaths = linkService.followLinksToLinks(vosPaths);
 
         ArchiveRequest archiveRequest = new ArchiveRequest();
         archiveRequest.setJobId(jobId);
diff --git a/src/main/java/it/inaf/oats/vospace/LinkService.java b/src/main/java/it/inaf/oats/vospace/LinkService.java
new file mode 100644
index 0000000000000000000000000000000000000000..e854a6225002ec3e8d461520fc869f450ff622ad
--- /dev/null
+++ b/src/main/java/it/inaf/oats/vospace/LinkService.java
@@ -0,0 +1,91 @@
+/*
+ * This file is part of vospace-rest
+ * Copyright (C) 2021 Istituto Nazionale di Astrofisica
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+package it.inaf.oats.vospace;
+
+import it.inaf.oats.vospace.datamodel.NodeUtils;
+import it.inaf.oats.vospace.exception.InternalFaultException;
+import it.inaf.oats.vospace.persistence.NodeDAO;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import net.ivoa.xml.vospace.v2.LinkNode;
+import net.ivoa.xml.vospace.v2.Node;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+@Service
+public class LinkService {
+
+    @Value("${vospace-authority}")
+    private String authority;
+
+    @Value("${link-max-depth}")
+    private int linkMaxDepth;
+    
+    @Autowired
+    private NodeDAO nodeDao;
+    
+    // Returns a new list = the list in argument with paths of links to links are substituted with 
+    // their actual destination vos path. Only links to external resources 
+    // (http:// ... ) should remain
+    public List<String> followLinksToLinks(List<String> vosPaths) {
+ 
+        List<LinkNode> linkNodesInPaths = nodeDao.returnLinkNodesInList(vosPaths);
+        
+        // No links no change
+        if(linkNodesInPaths.isEmpty())
+            return vosPaths;
+              
+        List<String> linkVosPaths = linkNodesInPaths.stream()
+                .map(ln -> NodeUtils.getVosPath(ln)).collect(Collectors.toList());
+        
+        // Safe copy of argument        
+        List<String> resultVosPaths = new ArrayList<>(vosPaths);
+        
+        resultVosPaths.removeAll(linkVosPaths);
+        
+        // follow links and add resulting vos paths. The only remaining links
+        // are expected to be external (http://... and so on)
+        
+        resultVosPaths.addAll(linkNodesInPaths.stream()
+               .map(ln -> NodeUtils.getVosPath(this.followLink(ln)))
+               .collect(Collectors.toList()));
+        
+        return resultVosPaths;                               
+    }
+    
+    public Node followLink(LinkNode linkNode) {
+        return this.followLinkRecursive(linkNode, 0);
+    }
+
+    private Node followLinkRecursive(LinkNode linkNode, int depth) {
+
+        if (depth >= linkMaxDepth) {
+            throw new InternalFaultException("Max link depth reached at link node: "
+                    + NodeUtils.getVosPath(linkNode));
+        }
+
+        String linkTarget = linkNode.getTarget();
+
+        if (URIUtils.isURIInternal(linkTarget)) {
+            String targetPath = URIUtils.returnVosPathFromNodeURI(linkTarget, authority);
+
+            Optional<Node> targetNodeOpt = nodeDao.listNode(targetPath);
+            Node targetNode = targetNodeOpt.orElseThrow(() -> new InternalFaultException("Broken Link to target: " + targetPath));
+
+            if (targetNode instanceof LinkNode) {
+                return this.followLinkRecursive(linkNode, ++depth);
+            } else {
+                return targetNode;
+            }
+        } else {
+            return linkNode;
+        }
+    }
+
+}
diff --git a/src/main/java/it/inaf/oats/vospace/UriService.java b/src/main/java/it/inaf/oats/vospace/UriService.java
index 3c2a1801ee0a5fda943df5a5bec76c1b0182a6ed..aacc7fa2c8bc477b7c50b7cfd3a8fab2797b9081 100644
--- a/src/main/java/it/inaf/oats/vospace/UriService.java
+++ b/src/main/java/it/inaf/oats/vospace/UriService.java
@@ -19,7 +19,7 @@ import it.inaf.oats.vospace.exception.NodeNotFoundException;
 import it.inaf.oats.vospace.exception.PermissionDeniedException;
 import it.inaf.oats.vospace.exception.ProtocolNotSupportedException;
 import it.inaf.oats.vospace.exception.NodeBusyException;
-import it.inaf.oats.vospace.persistence.LinkedServiceDAO;
+import it.inaf.oats.vospace.parent.persistence.LinkedServiceDAO;
 import it.inaf.oats.vospace.persistence.LocationDAO;
 import it.inaf.oats.vospace.persistence.NodeDAO;
 import it.inaf.oats.vospace.persistence.model.Location;
@@ -51,11 +51,11 @@ public class UriService {
     @Value("${file-service-url}")
     private String fileServiceUrl;
 
-    @Value("${link-max-depth}")
-    private int linkMaxDepth;
-
     @Autowired
     private NodeDAO nodeDao;
+    
+    @Autowired
+    private LinkService linkService;
 
     @Autowired
     private LocationDAO locationDAO;
@@ -158,7 +158,7 @@ public class UriService {
                     if (!NodeUtils.checkIfReadable(node, user.getName(), user.getGroups())) {
                         throw PermissionDeniedException.forPath(relativePath);
                     }
-                    node = this.followLink((LinkNode) node);
+                    node = linkService.followLink((LinkNode) node);
                 }
             }
             return node;
@@ -314,35 +314,6 @@ public class UriService {
         return (Transfer) job.getJobInfo().getAny().get(0);
     }
 
-    private Node followLink(LinkNode linkNode) {
-        return this.followLinkRecursive(linkNode, 0);
-    }
-
-    private Node followLinkRecursive(LinkNode linkNode, int depth) {
-
-        if (depth >= linkMaxDepth) {
-            throw new InternalFaultException("Max link depth reached at link node: "
-                    + NodeUtils.getVosPath(linkNode));
-        }
-
-        String linkTarget = linkNode.getTarget();
-
-        if (URIUtils.isURIInternal(linkTarget)) {
-            String targetPath = URIUtils.returnVosPathFromNodeURI(linkTarget, authority);
-
-            Optional<Node> targetNodeOpt = nodeDao.listNode(targetPath);
-            Node targetNode = targetNodeOpt.orElseThrow(() -> new InternalFaultException("Broken Link to target: " + targetPath));
-
-            if (targetNode instanceof LinkNode) {
-                return this.followLinkRecursive(linkNode, ++depth);
-            } else {
-                return targetNode;
-            }
-        } else {
-            return linkNode;
-        }
-    }
-
     public enum ProtocolType {
         // Please keep the URIs in this enum UNIQUE!
         // added a unit test to check this
diff --git a/src/main/java/it/inaf/oats/vospace/persistence/LinkedServiceDAO.java b/src/main/java/it/inaf/oats/vospace/persistence/LinkedServiceDAO.java
deleted file mode 100644
index 5af99100e53f21cfb02d046e1bd765aea3419065..0000000000000000000000000000000000000000
--- a/src/main/java/it/inaf/oats/vospace/persistence/LinkedServiceDAO.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * This file is part of vospace-rest
- * Copyright (C) 2021 Istituto Nazionale di Astrofisica
- * SPDX-License-Identifier: GPL-3.0-or-later
- */
-package it.inaf.oats.vospace.persistence;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import javax.sql.DataSource;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.jdbc.core.JdbcTemplate;
-import org.springframework.stereotype.Repository;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-@Repository
-public class LinkedServiceDAO {
-
-    private static final Logger LOG = LoggerFactory.getLogger(LinkedServiceDAO.class);
-
-    private static final ObjectMapper MAPPER = new ObjectMapper();
-
-    private final JdbcTemplate jdbcTemplate;
-
-    @Autowired
-    public LinkedServiceDAO(DataSource dataSource) {
-        jdbcTemplate = new JdbcTemplate(dataSource);
-    }
-
-    public boolean isLinkedServiceUrl(String targetUrl) {
-        String sql = " SELECT COUNT(*) > 0\n"
-                + "FROM linked_service\n"
-                + "WHERE ? LIKE service_base_url || '%'";
-        
-        return jdbcTemplate.query(sql, ps -> {
-            ps.setString(1, targetUrl);
-        }, row -> {
-            if (!row.next()) {
-                throw new IllegalStateException("Expected one result");
-            }
-            return row.getBoolean(1);
-        });
-    }
-}
diff --git a/src/main/java/it/inaf/oats/vospace/persistence/NodeDAO.java b/src/main/java/it/inaf/oats/vospace/persistence/NodeDAO.java
index ed25fc38d4124abc84ec3d2b5a2afd1f9ba1cf38..2da0a44870fcc5aa4921e040374f199ce89fa163 100644
--- a/src/main/java/it/inaf/oats/vospace/persistence/NodeDAO.java
+++ b/src/main/java/it/inaf/oats/vospace/persistence/NodeDAO.java
@@ -18,6 +18,7 @@ import java.sql.SQLException;
 import java.sql.Types;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -174,7 +175,7 @@ public class NodeDAO {
                 ps.setArray(++i, fromPropertyToArray(ps, NodeProperties.getNodePropertyByURI(newNode, NodeProperties.GROUP_WRITE_URI)));
                 ps.setBoolean(++i, Boolean.valueOf(NodeProperties.getNodePropertyByURI(newNode, NodeProperties.PUBLIC_READ_URI)));
                 if (isLinkNode) {
-                    ps.setString(++i, ((LinkNode) newNode).getTarget() );
+                    ps.setString(++i, ((LinkNode) newNode).getTarget());
                 }
                 ps.setString(++i, vosPath);
                 return ps;
@@ -552,6 +553,36 @@ public class NodeDAO {
         }
     }
 
+    public List<LinkNode> returnLinkNodesInList(List<String> vosPaths) {
+
+        if (vosPaths.isEmpty()) {
+            throw new IllegalArgumentException("Received empty list of paths");
+        }
+
+        String sql = "SELECT n.node_id, get_vos_path(n.node_id) as vos_path, n.name,\n"
+                + "n.type, n.async_trans, n.sticky, n.job_id IS NOT NULL AS busy_state, n.creator_id, n.group_read, n.group_write,\n"
+                + "n.is_public, n.content_length, n.created_on, n.last_modified, n.accept_views, n.provide_views, n.quota, n.content_md5, n.target\n"
+                + "FROM node n\n"
+                + "WHERE " + String.join(" OR ", Collections.nCopies(vosPaths.size(), "n.node_id  = id_from_vos_path(?)")) + "\n"
+                + "AND n.type = 'link'\n";
+
+        return jdbcTemplate.query(conn -> {
+            PreparedStatement ps = conn.prepareStatement(sql);
+            int i = 0;
+            for (String vosPath : vosPaths) {
+                ps.setString(++i, vosPath);
+            }
+            return ps;
+        }, rs -> {
+            List<LinkNode> linkNodes = new ArrayList<>();
+            while (rs.next()) {
+                linkNodes.add((LinkNode) this.getNodeFromResultSet(rs));
+            }
+            return linkNodes;
+        });
+
+    }
+
     private String getGroupsString(ResultSet rs, String column) throws SQLException {
         Array array = rs.getArray(column);
         if (array == null) {
diff --git a/src/test/java/it/inaf/oats/vospace/UriServiceTest.java b/src/test/java/it/inaf/oats/vospace/UriServiceTest.java
index 056bf98152b7eb080023da7faf7a5bd1490205d7..3b98d4fa219678f64c6cff19d9e9cf8527396c5b 100644
--- a/src/test/java/it/inaf/oats/vospace/UriServiceTest.java
+++ b/src/test/java/it/inaf/oats/vospace/UriServiceTest.java
@@ -16,7 +16,7 @@ import it.inaf.oats.vospace.exception.InvalidArgumentException;
 import it.inaf.oats.vospace.exception.NodeBusyException;
 import it.inaf.oats.vospace.exception.PermissionDeniedException;
 import it.inaf.oats.vospace.exception.ProtocolNotSupportedException;
-import it.inaf.oats.vospace.persistence.LinkedServiceDAO;
+import it.inaf.oats.vospace.parent.persistence.LinkedServiceDAO;
 import it.inaf.oats.vospace.persistence.LocationDAO;
 import it.inaf.oats.vospace.persistence.NodeDAO;
 import it.inaf.oats.vospace.persistence.model.Location;
diff --git a/src/test/java/it/inaf/oats/vospace/persistence/LinkedServiceDAOTest.java b/src/test/java/it/inaf/oats/vospace/persistence/LinkedServiceDAOTest.java
deleted file mode 100644
index c58ecbd96084ad3c529022f25c714074a60f9ff0..0000000000000000000000000000000000000000
--- a/src/test/java/it/inaf/oats/vospace/persistence/LinkedServiceDAOTest.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * This file is part of vospace-rest
- * Copyright (C) 2021 Istituto Nazionale di Astrofisica
- * SPDX-License-Identifier: GPL-3.0-or-later
- */
-package it.inaf.oats.vospace.persistence;
-
-import javax.sql.DataSource;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.test.context.ContextConfiguration;
-import org.springframework.test.context.TestPropertySource;
-import org.springframework.test.context.junit.jupiter.SpringExtension;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-
-@ExtendWith(SpringExtension.class)
-@ContextConfiguration(classes = {DataSourceConfig.class})
-@TestPropertySource(locations = "classpath:test.properties")
-public class LinkedServiceDAOTest {
-
-    @Autowired
-    private DataSource dataSource;
-    private LinkedServiceDAO dao;
-
-    @BeforeEach
-    public void init() {
-        dao = new LinkedServiceDAO(dataSource);
-    }
-    
-    @Test
-    void testIsLinkedService() {
-        assertTrue(dao.isLinkedServiceUrl("http://archives.ia2.inaf.it/files/aao/pippofile.fits.gz"));
-        assertFalse(dao.isLinkedServiceUrl("http://noportal.ia2.inaf.it/files/nop/nopippofile.tar.gz"));
-    }
-
-}