diff --git a/src/libstore/binary-cache-store.cc b/src/libstore/binary-cache-store.cc
index 9226c4e19..a26770c79 100644
--- a/src/libstore/binary-cache-store.cc
+++ b/src/libstore/binary-cache-store.cc
@@ -331,6 +331,17 @@ bool BinaryCacheStore::isValidPathUncached(const StorePath & storePath)
     return fileExists(narInfoFileFor(storePath));
 }
 
+std::optional<StorePath> BinaryCacheStore::queryPathFromHashPart(const std::string & hashPart)
+{
+    auto pseudoPath = StorePath(hashPart + "-" + MissingName);
+    try {
+        auto info = queryPathInfo(pseudoPath);
+        return info->path;
+    } catch (InvalidPath &) {
+        return std::nullopt;
+    }
+}
+
 void BinaryCacheStore::narFromPath(const StorePath & storePath, Sink & sink)
 {
     auto info = queryPathInfo(storePath).cast<const NarInfo>();
diff --git a/src/libstore/binary-cache-store.hh b/src/libstore/binary-cache-store.hh
index ca538b3cb..8c82e2387 100644
--- a/src/libstore/binary-cache-store.hh
+++ b/src/libstore/binary-cache-store.hh
@@ -95,8 +95,7 @@ public:
     void queryPathInfoUncached(const StorePath & path,
         Callback<std::shared_ptr<const ValidPathInfo>> callback) noexcept override;
 
-    std::optional<StorePath> queryPathFromHashPart(const std::string & hashPart) override
-    { unsupported("queryPathFromHashPart"); }
+    std::optional<StorePath> queryPathFromHashPart(const std::string & hashPart) override;
 
     void addToStore(const ValidPathInfo & info, Source & narSource,
         RepairFlag repair, CheckSigsFlag checkSigs) override;
diff --git a/src/nix/path-from-hash-part.cc b/src/nix/path-from-hash-part.cc
new file mode 100644
index 000000000..7f7cda8d3
--- /dev/null
+++ b/src/nix/path-from-hash-part.cc
@@ -0,0 +1,39 @@
+#include "command.hh"
+#include "store-api.hh"
+
+using namespace nix;
+
+struct CmdPathFromHashPart : StoreCommand
+{
+    std::string hashPart;
+
+    CmdPathFromHashPart()
+    {
+        expectArgs({
+            .label = "hash-part",
+            .handler = {&hashPart},
+        });
+    }
+
+    std::string description() override
+    {
+        return "get a store path from its hash part";
+    }
+
+    std::string doc() override
+    {
+        return
+          #include "path-from-hash-part.md"
+          ;
+    }
+
+    void run(ref<Store> store) override
+    {
+        if (auto storePath = store->queryPathFromHashPart(hashPart))
+            logger->cout(store->printStorePath(*storePath));
+        else
+            throw Error("there is no store path corresponding to '%s'", hashPart);
+    }
+};
+
+static auto rCmdPathFromHashPart = registerCommand2<CmdPathFromHashPart>({"store", "path-from-hash-part"});
diff --git a/src/nix/path-from-hash-part.md b/src/nix/path-from-hash-part.md
new file mode 100644
index 000000000..788e13ab6
--- /dev/null
+++ b/src/nix/path-from-hash-part.md
@@ -0,0 +1,20 @@
+R""(
+
+# Examples
+
+* Return the full store path with the given hash part:
+
+  ```console
+  # nix store path-from-hash-part --store https://cache.nixos.org/ 0i2jd68mp5g6h2sa5k9c85rb80sn8hi9
+  /nix/store/0i2jd68mp5g6h2sa5k9c85rb80sn8hi9-hello-2.10
+  ```
+
+# Description
+
+Given the hash part of a store path (that is, the 32 characters
+following `/nix/store/`), return the full store path. This is
+primarily useful in the implementation of binary caches, where a
+request for a `.narinfo` file only supplies the hash part
+(e.g. `https://cache.nixos.org/0i2jd68mp5g6h2sa5k9c85rb80sn8hi9.narinfo`).
+
+)""
diff --git a/tests/local.mk b/tests/local.mk
index 5e48ceae1..340817ec3 100644
--- a/tests/local.mk
+++ b/tests/local.mk
@@ -109,7 +109,8 @@ nix_tests = \
   store-ping.sh \
   fetchClosure.sh \
   completions.sh \
-  impure-derivations.sh
+  impure-derivations.sh \
+  path-from-hash-part.sh
 
 ifeq ($(HAVE_LIBCPUID), 1)
 	nix_tests += compute-levels.sh
diff --git a/tests/path-from-hash-part.sh b/tests/path-from-hash-part.sh
new file mode 100644
index 000000000..bdd104434
--- /dev/null
+++ b/tests/path-from-hash-part.sh
@@ -0,0 +1,10 @@
+source common.sh
+
+path=$(nix build --no-link --print-out-paths -f simple.nix)
+
+hash_part=$(basename $path)
+hash_part=${hash_part:0:32}
+
+path2=$(nix store path-from-hash-part $hash_part)
+
+[[ $path = $path2 ]]