diff --git a/src/libstore/path-regex.hh b/src/libstore/path-regex.hh
index 4f8dc4c1f..a44e6a2eb 100644
--- a/src/libstore/path-regex.hh
+++ b/src/libstore/path-regex.hh
@@ -3,6 +3,6 @@
 
 namespace nix {
 
-static constexpr std::string_view nameRegexStr = R"([0-9a-zA-Z\+\-\._\?=]+)";
+static constexpr std::string_view nameRegexStr = R"([0-9a-zA-Z\+\-_\?=][0-9a-zA-Z\+\-\._\?=]*)";
 
 }
diff --git a/src/libstore/path.cc b/src/libstore/path.cc
index 552e83114..3c6b9fc10 100644
--- a/src/libstore/path.cc
+++ b/src/libstore/path.cc
@@ -11,6 +11,8 @@ static void checkName(std::string_view path, std::string_view name)
     if (name.size() > StorePath::MaxPathLen)
         throw BadStorePath("store path '%s' has a name longer than %d characters",
             path, StorePath::MaxPathLen);
+    if (name[0] == '.')
+        throw BadStorePath("store path '%s' starts with illegal character '.'", path);
     // See nameRegexStr for the definition
     for (auto c : name)
         if (!((c >= '0' && c <= '9')
diff --git a/src/libstore/tests/path.cc b/src/libstore/tests/path.cc
index efa35ef2b..5a84d646c 100644
--- a/src/libstore/tests/path.cc
+++ b/src/libstore/tests/path.cc
@@ -39,6 +39,7 @@ TEST_DONT_PARSE(double_star, "**")
 TEST_DONT_PARSE(star_first, "*,foo")
 TEST_DONT_PARSE(star_second, "foo,*")
 TEST_DONT_PARSE(bang, "foo!o")
+TEST_DONT_PARSE(dotfile, ".gitignore")
 
 #undef TEST_DONT_PARSE
 
@@ -101,8 +102,12 @@ Gen<StorePathName> Arbitrary<StorePathName>::arbitrary()
                 pre += '-';
                 break;
             case 64:
-                pre += '.';
-                break;
+                // names aren't permitted to start with a period,
+                // so just fall through to the next case here
+                if (c != 0) {
+                    pre += '.';
+                    break;
+                }
             case 65:
                 pre += '_';
                 break;