--- a/src/common/rclconfig.cpp
+++ b/src/common/rclconfig.cpp
@@ -1318,38 +1318,118 @@
     return path_cat(getCacheDir(), "index.pid");
 }
 
+/* Eliminate the common leaf part of file paths p1 and p2. Example: 
+ * /mnt1/common/part /mnt2/common/part -> /mnt1 /mnt2. This is used
+ * for computing translations for paths when the dataset has been
+ * moved. Of course this could be done more efficiently than by splitting 
+ * into vectors, but we don't care.*/
+static string path_diffstems(const string& p1, const string& p2,
+                            string& r1, string& r2)
+{
+    string reason;
+    r1.clear();
+    r2.clear();
+    vector<string> v1, v2;
+    stringToTokens(p1, v1, "/");
+    stringToTokens(p2, v2, "/");
+    unsigned int l1 = v1.size();
+    unsigned int l2 = v2.size();
+        
+    // Search for common leaf part
+    unsigned int cl = 0;
+    for (; cl < MIN(l1, l2); cl++) {
+        if (v1[l1-cl-1] != v2[l2-cl-1]) {
+            break;
+        }
+    }
+    //cerr << "Common length = " << cl << endl;
+    if (cl == 0) {
+        reason = "Input paths are empty or have no common part";
+        return reason;
+    }
+    for (unsigned i = 0; i < l1 - cl; i++) {
+        r1 += "/" + v1[i];
+    }
+    for (unsigned i = 0; i < l2 - cl; i++) {
+        r2 += "/" + v2[i];
+    }
+        
+    return reason;
+}
+
 void RclConfig::urlrewrite(const string& dbdir, string& url) const
 {
-    LOGDEB2("RclConfig::urlrewrite: dbdir [" << dbdir << "] url [" << url <<
+    LOGDEB("RclConfig::urlrewrite: dbdir [" << dbdir << "] url [" << url <<
             "]\n");
 
+    // If orgidxconfdir is set, we assume that this index is for a
+    // movable dataset, with the configuration directory stored inside
+    // the dataset tree. This allows computing automatic path
+    // translations if the dataset has been moved.
+    string orig_confdir;
+    string cur_confdir;
+    string confstemorg, confstemrep;
+    if (m_conf->get("orgidxconfdir", orig_confdir, "")) {
+        if (!m_conf->get("curidxconfdir", cur_confdir, "")) {
+            cur_confdir = m_confdir;
+        }
+        LOGDEB("RclConfig::urlrewrite: orgidxconfdir: " << orig_confdir <<
+               " cur_confdir " << cur_confdir << endl);
+        string reason = path_diffstems(orig_confdir, cur_confdir,
+                                       confstemorg, confstemrep);
+        if (!reason.empty()) {
+            LOGERR("urlrewrite: path_diffstems failed: " << reason <<
+                   " : orig_confdir [" << orig_confdir <<
+                   "] cur_confdir [" << cur_confdir << endl);
+            confstemorg = confstemrep = "";
+        }
+    }
+    
     // Do path translations exist for this index ?
+    bool needptrans = true;
     if (m_ptrans == 0 || !m_ptrans->hasSubKey(dbdir)) {
 	LOGDEB2("RclConfig::urlrewrite: no paths translations (m_ptrans " <<
                 m_ptrans << ")\n");
-	return;
-    }
-
+        needptrans = false;
+    }
+
+    if (!needptrans && confstemorg.empty()) {
+        return;
+    }
+    bool computeurl = false;
+    
     string path = fileurltolocalpath(url);
     if (path.empty()) {
 	LOGDEB2("RclConfig::urlrewrite: not file url\n");
 	return;
     }
-
-    // For each translation check if the prefix matches the input path,
-    // replace and return the result if it does.
-    vector<string> opaths = m_ptrans->getNames(dbdir);
-    for (vector<string>::const_iterator it = opaths.begin(); 
-	 it != opaths.end(); it++) {
-	if (it->size() <= path.size() && !path.compare(0, it->size(), *it)) {
-	    string npath;
-	    // This call always succeeds because the key comes from getNames()
-	    if (m_ptrans->get(*it, npath, dbdir)) { 
-		path = path.replace(0, it->size(), npath);
-		url = path_pathtofileurl(path);
-	    }
-	    break;
-	}
+    
+    // Do the movable volume thing.
+    if (!confstemorg.empty() && confstemorg.size() <= path.size() &&
+        !path.compare(0, confstemorg.size(), confstemorg)) {
+        path = path.replace(0, confstemorg.size(), confstemrep);
+        computeurl = true;
+    }
+
+    if (needptrans) {
+        // For each translation check if the prefix matches the input path,
+        // replace and return the result if it does.
+        vector<string> opaths = m_ptrans->getNames(dbdir);
+        for (const auto& opath: opaths) {
+            if (opath.size() <= path.size() &&
+                !path.compare(0, opath.size(), opath)) {
+                string npath;
+                // Key comes from getNames()=> call must succeed
+                if (m_ptrans->get(opath, npath, dbdir)) { 
+                    path = path.replace(0, opath.size(), npath);
+                    computeurl = true;
+                }
+                break;
+            }
+        }
+    }
+    if (computeurl) {
+        url = path_pathtofileurl(path);
     }
 }