diff --git a/src/infrastructure/util/PhabricatorSlug.php b/src/infrastructure/util/PhabricatorSlug.php
--- a/src/infrastructure/util/PhabricatorSlug.php
+++ b/src/infrastructure/util/PhabricatorSlug.php
@@ -10,6 +10,24 @@
     $slug = preg_replace('@_+@', '_', $slug);
     $slug = trim($slug, '_');
 
+    // Specifically rewrite these slugs. It's OK to have a slug like "a..b",
+    // but not a slug which is only "..".
+
+    // NOTE: These are explicitly not pht()'d, because they should be stable
+    // across languages.
+
+    $replace = array(
+      '.'   => 'dot',
+      '..'  => 'dotdot',
+    );
+
+    foreach ($replace as $pattern => $replacement) {
+      $pattern = preg_quote($pattern, '@');
+      $slug = preg_replace(
+        '@(^|/)'.$pattern.'(\z|/)@',
+        '\1'.$replacement.'\2', $slug);
+    }
+
     return $slug.'/';
   }
 
diff --git a/src/infrastructure/util/__tests__/PhabricatorSlugTestCase.php b/src/infrastructure/util/__tests__/PhabricatorSlugTestCase.php
--- a/src/infrastructure/util/__tests__/PhabricatorSlugTestCase.php
+++ b/src/infrastructure/util/__tests__/PhabricatorSlugTestCase.php
@@ -17,6 +17,16 @@
       "T\x00O\x00D\x00O"  => "t_o_d_o/",
       'x#%&+=\\?<> y'     => 'x_y/',
       "\xE2\x98\x83"      => "\xE2\x98\x83/",
+      '..'                => 'dotdot/',
+      '../'               => 'dotdot/',
+      '/../'              => 'dotdot/',
+      'a/b'               => 'a/b/',
+      'a//b'              => 'a/b/',
+      'a/../b/'           => 'a/dotdot/b/',
+      '/../a'             => 'dotdot/a/',
+      '../a'              => 'dotdot/a/',
+      'a/..'              => 'a/dotdot/',
+      'a/../'             => 'a/dotdot/',
     );
 
     foreach ($slugs as $slug => $normal) {