diff --git a/django/core/files/storage.py b/django/core/files/storage.py
index cff2d06f1a..7a9bc06665 100644
--- a/django/core/files/storage.py
+++ b/django/core/files/storage.py
@@ -277,11 +277,7 @@ class FileSystemStorage(Storage):
         return directories, files
 
     def path(self, name):
-        try:
-            path = safe_join(self.location, name)
-        except ValueError:
-            raise SuspiciousFileOperation("Attempted access to '%s' denied." % name)
-        return os.path.normpath(path)
+        return safe_join(self.location, name)
 
     def size(self, name):
         return os.path.getsize(self.path(name))
diff --git a/django/template/loaders/app_directories.py b/django/template/loaders/app_directories.py
index b66adaed08..cab1edf8cc 100644
--- a/django/template/loaders/app_directories.py
+++ b/django/template/loaders/app_directories.py
@@ -8,6 +8,7 @@ import sys
 
 from django.apps import apps
 from django.conf import settings
+from django.core.exceptions import SuspiciousFileOperation
 from django.template.base import TemplateDoesNotExist
 from django.template.loader import BaseLoader
 from django.utils._os import safe_join
@@ -47,11 +48,9 @@ class Loader(BaseLoader):
         for template_dir in template_dirs:
             try:
                 yield safe_join(template_dir, template_name)
-            except UnicodeDecodeError:
-                # The template dir name was a bytestring that wasn't valid UTF-8.
-                raise
-            except ValueError:
-                # The joined path was located outside of template_dir.
+            except SuspiciousFileOperation:
+                # The joined path was located outside of this template_dir
+                # (it might be inside another one, so this isn't fatal).
                 pass
 
     def load_template_source(self, template_name, template_dirs=None):
diff --git a/django/template/loaders/filesystem.py b/django/template/loaders/filesystem.py
index 52e41ef0b9..dc7de84abf 100644
--- a/django/template/loaders/filesystem.py
+++ b/django/template/loaders/filesystem.py
@@ -3,6 +3,7 @@ Wrapper for loading templates from the filesystem.
 """
 
 from django.conf import settings
+from django.core.exceptions import SuspiciousFileOperation
 from django.template.base import TemplateDoesNotExist
 from django.template.loader import BaseLoader
 from django.utils._os import safe_join
@@ -22,13 +23,9 @@ class Loader(BaseLoader):
         for template_dir in template_dirs:
             try:
                 yield safe_join(template_dir, template_name)
-            except UnicodeDecodeError:
-                # The template dir name was a bytestring that wasn't valid UTF-8.
-                raise
-            except ValueError:
-                # The joined path was located outside of this particular
-                # template_dir (it might be inside another one, so this isn't
-                # fatal).
+            except SuspiciousFileOperation:
+                # The joined path was located outside of this template_dir
+                # (it might be inside another one, so this isn't fatal).
                 pass
 
     def load_template_source(self, template_name, template_dirs=None):
diff --git a/django/utils/_os.py b/django/utils/_os.py
index 1d7ddf619e..bcfe3de636 100644
--- a/django/utils/_os.py
+++ b/django/utils/_os.py
@@ -4,6 +4,7 @@ import sys
 import tempfile
 from os.path import join, normcase, normpath, abspath, isabs, sep, dirname
 
+from django.core.exceptions import SuspiciousFileOperation
 from django.utils.encoding import force_text
 from django.utils import six
 
@@ -77,8 +78,9 @@ def safe_join(base, *paths):
     if (not normcase(final_path).startswith(normcase(base_path + sep)) and
             normcase(final_path) != normcase(base_path) and
             dirname(normcase(base_path)) != normcase(base_path)):
-        raise ValueError('The joined path (%s) is located outside of the base '
-                         'path component (%s)' % (final_path, base_path))
+        raise SuspiciousFileOperation(
+            'The joined path ({}) is located outside of the base path '
+            'component ({})'.format(final_path, base_path))
     return final_path
 
 
diff --git a/tests/utils_tests/test_os_utils.py b/tests/utils_tests/test_os_utils.py
index da54e93ac0..7e6c74b3da 100644
--- a/tests/utils_tests/test_os_utils.py
+++ b/tests/utils_tests/test_os_utils.py
@@ -1,6 +1,7 @@
 import os
 import unittest
 
+from django.core.exceptions import SuspiciousFileOperation
 from django.utils._os import safe_join
 
 
@@ -24,3 +25,7 @@ class SafeJoinTests(unittest.TestCase):
             path,
             os.path.sep,
         )
+
+    def test_parent_path(self):
+        with self.assertRaises(SuspiciousFileOperation):
+            safe_join("/abc/", "../def")