diff --git a/django/contrib/staticfiles/handlers.py b/django/contrib/staticfiles/handlers.py
index 5174586..f041f74 100644
--- a/django/contrib/staticfiles/handlers.py
+++ b/django/contrib/staticfiles/handlers.py
@@ -7,12 +7,12 @@ except ImportError:     # Python 2
 
 from django.conf import settings
 from django.core.handlers.base import get_path_info
-from django.core.handlers.wsgi import WSGIHandler
+from django.core.handlers.wsgi import WSGIHandler, UrlPrefixAwareMixin
 
 from django.contrib.staticfiles import utils
 from django.contrib.staticfiles.views import serve
 
-class StaticFilesHandler(WSGIHandler):
+class StaticFilesHandler(UrlPrefixAwareMixin, WSGIHandler):
     """
     WSGI middleware that intercepts calls to the static files directory, as
     defined by the STATIC_URL setting, and serves those files.
@@ -31,7 +31,7 @@ class StaticFilesHandler(WSGIHandler):
 
     def get_base_url(self):
         utils.check_settings()
-        return settings.STATIC_URL
+        return self.fix_path(settings.STATIC_URL)
 
     def _should_handle(self, path):
         """
@@ -53,12 +53,13 @@ class StaticFilesHandler(WSGIHandler):
         """
         Actually serves the request path.
         """
-        return serve(request, self.file_path(request.path), insecure=True)
+        path = self.fix_path(request.path)
+        return serve(request, self.file_path(path), insecure=True)
 
     def get_response(self, request):
         from django.http import Http404
 
-        if self._should_handle(request.path):
+        if self._should_handle(self.fix_path(request.path)):
             try:
                 return self.serve(request)
             except Http404 as e:
diff --git a/django/core/handlers/wsgi.py b/django/core/handlers/wsgi.py
index a9fa094..c25249c 100644
--- a/django/core/handlers/wsgi.py
+++ b/django/core/handlers/wsgi.py
@@ -266,3 +266,61 @@ class WSGIHandler(base.BaseHandler):
             response_headers.append((str('Set-Cookie'), str(c.output(header=''))))
         start_response(force_str(status), response_headers)
         return response
+
+
+class UrlPrefixAwareMixin(object):
+    _url_prefix = None
+    _url_prefix_fetched = False
+    
+    @property
+    def url_prefix(self):
+        """
+        Get the FORCE_SCRIPT_NAME from settings or None
+        """
+        if not self._url_prefix_fetched:
+            from django.conf import settings
+            self._url_prefix = getattr(settings, 'FORCE_SCRIPT_NAME', None)
+            self._url_prefix_fetched = True
+        return self._url_prefix
+
+    def fix_path(self, path):
+        """
+        Strips FORCE_SCRIPT_NAME from the front of the url
+        """
+        if self.url_prefix and path.startswith(self.url_prefix):
+            path = path[len(self.url_prefix):]
+        return path
+    
+    def is_prefixed(self, path):
+        return not(self.url_prefix) or path.startswith(self.url_prefix)
+
+
+class UrlPrefixAwareHandler(UrlPrefixAwareMixin, WSGIHandler):
+    """
+    WSGI middleware that does nothing if FORCE_SCRIPT_NAME is not defined.
+    
+    When FORCE_SCRIPT_NAME is defined:
+      * this middleware will strip it from PATH_INFO in the environ before
+        passing environ to the next handler
+      * requests that do not begin with FORCE_SCRIPT_NAME will receive a
+        400 Bad Request response
+    """
+    def __init__(self, application):
+        self.application = application
+        super(UrlPrefixAwareHandler, self).__init__()
+
+    def get_response(self, request):
+        logger.warning('Bad Request (Prefixed url required when using FORCE_SCRIPT_NAME)',
+            extra={
+                'status_code': 400,
+            }
+        )
+        return http.HttpResponseBadRequest()
+
+    def __call__(self, environ, start_response):
+        if self.url_prefix:
+            path_info = base.get_path_info(environ)
+            if not self.is_prefixed(path_info):
+                return super(UrlPrefixAwareHandler, self).__call__(environ, start_response)
+            environ['PATH_INFO'] = self.fix_path(path_info)
+        return self.application(environ, start_response)
diff --git a/django/core/management/commands/runserver.py b/django/core/management/commands/runserver.py
index 391e0b4..a4f71be 100644
--- a/django/core/management/commands/runserver.py
+++ b/django/core/management/commands/runserver.py
@@ -7,6 +7,7 @@ import socket
 
 from django.core.management.base import BaseCommand, CommandError
 from django.core.servers.basehttp import run, WSGIServerException, get_internal_wsgi_application
+from django.core.handlers.wsgi import UrlPrefixAwareHandler
 from django.utils import autoreload
 
 naiveip_re = re.compile(r"""^(?:
@@ -110,6 +111,7 @@ class Command(BaseCommand):
 
         try:
             handler = self.get_handler(*args, **options)
+            handler = UrlPrefixAwareHandler(handler)
             run(self.addr, int(self.port), handler,
                 ipv6=self.use_ipv6, threading=threading)
         except WSGIServerException as e:
diff --git a/django/test/signals.py b/django/test/signals.py
index a96bdff..3e8caf2 100644
--- a/django/test/signals.py
+++ b/django/test/signals.py
@@ -79,3 +79,9 @@ def file_storage_changed(**kwargs):
     if kwargs['setting'] in ('MEDIA_ROOT', 'DEFAULT_FILE_STORAGE'):
         from django.core.files.storage import default_storage
         default_storage._wrapped = empty
+
+@receiver(setting_changed)
+def fix_script_prefix(**kwargs):
+    if kwargs['setting'] == 'FORCE_SCRIPT_NAME':
+        from django.core.urlresolvers import set_script_prefix
+        set_script_prefix('/')
\ No newline at end of file
diff --git a/django/test/testcases.py b/django/test/testcases.py
index c311540..12b2151 100644
--- a/django/test/testcases.py
+++ b/django/test/testcases.py
@@ -17,6 +17,7 @@ import threading
 import errno
 
 from django.conf import settings
+from django.core.handlers.wsgi import UrlPrefixAwareHandler
 from django.contrib.staticfiles.handlers import StaticFilesHandler
 from django.core import mail
 from django.core.exceptions import ValidationError, ImproperlyConfigured
@@ -1014,10 +1015,11 @@ class _MediaFilesHandler(StaticFilesHandler):
         return settings.MEDIA_ROOT
 
     def get_base_url(self):
-        return settings.MEDIA_URL
+        return self.fix_path(settings.MEDIA_URL)
 
     def serve(self, request):
-        relative_url = request.path[len(self.base_url[2]):]
+        path = self.fix_path(request.path)
+        relative_url = path[len(self.base_url[2]):]
         return serve(request, relative_url, document_root=self.get_base_dir())
 
 
@@ -1049,6 +1051,7 @@ class LiveServerThread(threading.Thread):
         try:
             # Create the handler for serving static and media files
             handler = StaticFilesHandler(_MediaFilesHandler(WSGIHandler()))
+            handler = UrlPrefixAwareHandler(handler)
 
             # Go through the list of possible ports, hoping that we can find
             # one that is free to use for the WSGI server.
diff --git a/tests/regressiontests/handlers/tests.py b/tests/regressiontests/handlers/tests.py
index 3557a63..5e92062 100644
--- a/tests/regressiontests/handlers/tests.py
+++ b/tests/regressiontests/handlers/tests.py
@@ -1,4 +1,4 @@
-from django.core.handlers.wsgi import WSGIHandler
+from django.core.handlers.wsgi import WSGIHandler, UrlPrefixAwareHandler
 from django.core import signals
 from django.test import RequestFactory, TestCase
 from django.test.utils import override_settings
@@ -58,3 +58,20 @@ class SignalsTests(TestCase):
         self.assertEqual(self.signals, ['started'])
         self.assertEqual(b''.join(response.streaming_content), b"streaming content")
         self.assertEqual(self.signals, ['started', 'finished'])
+
+
+@override_settings(FORCE_SCRIPT_NAME='/prefixed-example')
+class PrefixedHandlerTests(TestCase):
+    urls = 'regressiontests.handlers.urls'
+
+    def test_unprefixed_path(self):
+        environ = RequestFactory().get('/regular/').environ
+        handler = UrlPrefixAwareHandler(WSGIHandler())
+        response = handler(environ, lambda *a, **k: None)
+        self.assertEqual(response.status_code, 400)
+
+    def test_prefix_path(self):
+        environ = RequestFactory().get('/prefixed-example/regular/').environ
+        handler = UrlPrefixAwareHandler(WSGIHandler())
+        response = handler(environ, lambda *a, **k: None)
+        self.assertEqual(response.content, b"regular content")
diff --git a/tests/regressiontests/servers/tests.py b/tests/regressiontests/servers/tests.py
index 1a7552e..084fd78 100644
--- a/tests/regressiontests/servers/tests.py
+++ b/tests/regressiontests/servers/tests.py
@@ -144,6 +144,70 @@ class LiveServerViews(LiveServerBase):
         self.assertIn(b"QUERY_STRING: 'q=%D1%82%D0%B5%D1%81%D1%82'", f.read())
 
 
+class LiveServerUrlPrefixedViews(LiveServerBase):
+    
+    @classmethod
+    def setUpClass(cls):
+        # Override settings
+        newsettings = {}
+        newsettings.update(TEST_SETTINGS)
+        newsettings.update({
+            'FORCE_SCRIPT_NAME': '/live-example',
+            'MEDIA_URL': '/live-example/media/',
+            'STATIC_URL': '/live-example/static/',
+        })
+        cls.settings_override = override_settings(**newsettings)
+        cls.settings_override.enable()
+        super(LiveServerBase, cls).setUpClass()
+
+    def test_bad_request(self):
+        """
+        Ensure that the LiveServerTestCase serves 400s.
+        """
+        try:
+            self.urlopen('/')
+        except HTTPError as err:
+            self.assertEqual(err.code, 400, 'Expected 400 response')
+        else:
+            self.fail('Expected 400 response')
+    
+    def test_404(self):
+        """
+        Ensure that the LiveServerTestCase serves 404s.
+        Refs #2879.
+        """
+        try:
+            self.urlopen('/live-example/')
+        except HTTPError as err:
+            self.assertEqual(err.code, 404, 'Expected 404 response')
+        else:
+            self.fail('Expected 404 response')
+
+    def test_view(self):
+        """
+        Ensure that the LiveServerTestCase serves views.
+        Refs #2879.
+        """
+        f = self.urlopen('/live-example/example_view/')
+        self.assertEqual(f.read(), b'example view')
+
+    def test_static_files(self):
+        """
+        Ensure that the LiveServerTestCase serves static files.
+        Refs #2879.
+        """
+        f = self.urlopen('/live-example/static/example_static_file.txt')
+        self.assertEqual(f.read().rstrip(b'\r\n'), b'example static file')
+
+    def test_media_files(self):
+        """
+        Ensure that the LiveServerTestCase serves media files.
+        Refs #2879.
+        """
+        f = self.urlopen('/live-example/media/example_media_file.txt')
+        self.assertEqual(f.read().rstrip(b'\r\n'), b'example media file')
+
+
 class LiveServerDatabase(LiveServerBase):
 
     def test_fixtures_loaded(self):
