diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py
index e068f41..ec7b066 100644
--- a/django/conf/global_settings.py
+++ b/django/conf/global_settings.py
@@ -527,6 +529,12 @@ PASSWORD_HASHERS = (
     'django.contrib.auth.hashers.CryptPasswordHasher',
 )
 
+#########
+# SITES #
+#########
+
+SITES_SITE_MODEL = 'sites.Site'
+
 ###########
 # SIGNING #
 ###########
diff --git a/django/contrib/auth/views.py b/django/contrib/auth/views.py
index c55f243..86d1e0f 100644
--- a/django/contrib/auth/views.py
+++ b/django/contrib/auth/views.py
@@ -16,7 +16,7 @@ from django.contrib.auth import REDIRECT_FIELD_NAME, login as auth_login, logout
 from django.contrib.auth.decorators import login_required
 from django.contrib.auth.forms import AuthenticationForm, PasswordResetForm, SetPasswordForm, PasswordChangeForm
 from django.contrib.auth.tokens import default_token_generator
-from django.contrib.sites.models import get_current_site
+from django.contrib.sites import get_current_site
 
 
 @sensitive_post_parameters()
diff --git a/django/contrib/auth/forms.py b/django/contrib/auth/forms.py
index 081d9e5..16f4bf2 100644
--- a/django/contrib/auth/forms.py
+++ b/django/contrib/auth/forms.py
@@ -17,7 +17,7 @@ from django.contrib.auth import authenticate, get_user_model
 from django.contrib.auth.models import User
 from django.contrib.auth.hashers import UNUSABLE_PASSWORD_PREFIX, identify_hasher
 from django.contrib.auth.tokens import default_token_generator
-from django.contrib.sites.models import get_current_site
+from django.contrib.sites import get_current_site
 
 
 UNMASKED_DIGITS_TO_SHOW = 6
diff --git a/django/contrib/contenttypes/views.py b/django/contrib/contenttypes/views.py
index 6d4a71928..f829da9 100644
--- a/django/contrib/contenttypes/views.py
+++ b/django/contrib/contenttypes/views.py
@@ -2,7 +2,7 @@ from __future__ import unicode_literals
 
 from django import http
 from django.contrib.contenttypes.models import ContentType
-from django.contrib.sites.models import Site, get_current_site
+from django.contrib.sites import get_site_model, get_current_site
 from django.core.exceptions import ObjectDoesNotExist
 from django.utils.translation import ugettext as _
 
@@ -39,6 +39,7 @@ def shortcut(request, content_type_id, object_id):
     # relation to the Site object
     object_domain = None
 
+    Site = get_site_model()
     if Site._meta.installed:
         opts = obj._meta
 
diff --git a/django/contrib/comments/feeds.py b/django/contrib/comments/feeds.py
index 2e0d4c3..eee20c0 100644
--- a/django/contrib/comments/feeds.py
+++ b/django/contrib/comments/feeds.py
@@ -1,5 +1,5 @@
 from django.contrib.syndication.views import Feed
-from django.contrib.sites.models import get_current_site
+from django.contrib.sites import get_current_site
 from django.contrib import comments
 from django.utils.translation import ugettext as _
 
diff --git a/django/contrib/comments/models.py b/django/contrib/comments/models.py
index bc4d932..9bbcb7a 100644
--- a/django/contrib/comments/models.py
+++ b/django/contrib/comments/models.py
@@ -2,7 +2,6 @@ from django.conf import settings
 from django.contrib.comments.managers import CommentManager
 from django.contrib.contenttypes import generic
 from django.contrib.contenttypes.models import ContentType
-from django.contrib.sites.models import Site
 from django.core import urlresolvers
 from django.db import models
 from django.utils.translation import ugettext_lazy as _
@@ -26,7 +25,7 @@ class BaseCommentAbstractModel(models.Model):
     content_object = generic.GenericForeignKey(ct_field="content_type", fk_field="object_pk")
 
     # Metadata about the comment
-    site = models.ForeignKey(Site)
+    site = models.ForeignKey(settings.SITES_SITE_MODEL)
 
     class Meta:
         abstract = True
diff --git a/django/contrib/comments/moderation.py b/django/contrib/comments/moderation.py
index 6648aeb..8fa0c14 100644
--- a/django/contrib/comments/moderation.py
+++ b/django/contrib/comments/moderation.py
@@ -62,7 +62,7 @@ from django.contrib.comments import signals
 from django.db.models.base import ModelBase
 from django.template import Context, loader
 from django.contrib import comments
-from django.contrib.sites.models import get_current_site
+from django.contrib.sites import get_current_site
 from django.utils import timezone
 
 class AlreadyModerated(Exception):
diff --git a/django/contrib/flatpages/models.py b/django/contrib/flatpages/models.py
index 42bb3ad..60c1831 100644
--- a/django/contrib/flatpages/models.py
+++ b/django/contrib/flatpages/models.py
@@ -1,7 +1,7 @@
 from __future__ import unicode_literals
 
 from django.db import models
-from django.contrib.sites.models import Site
+from django.conf import settings
 from django.core.urlresolvers import get_script_prefix
 from django.utils.translation import ugettext_lazy as _
 from django.utils.encoding import iri_to_uri, python_2_unicode_compatible
@@ -17,7 +17,7 @@ class FlatPage(models.Model):
     registration_required = models.BooleanField(_('registration required'),
         help_text=_("If this is checked, only logged-in users will be able to view the page."),
         default=False)
-    sites = models.ManyToManyField(Site)
+    sites = models.ManyToManyField(settings.SITES_SITE_MODEL)
 
     class Meta:
         db_table = 'django_flatpage'
diff --git a/django/contrib/flatpages/views.py b/django/contrib/flatpages/views.py
index 20e930f..34d39cb 100644
--- a/django/contrib/flatpages/views.py
+++ b/django/contrib/flatpages/views.py
@@ -1,6 +1,6 @@
 from django.conf import settings
 from django.contrib.flatpages.models import FlatPage
-from django.contrib.sites.models import get_current_site
+from django.contrib.sites import get_current_site
 from django.http import Http404, HttpResponse, HttpResponsePermanentRedirect
 from django.shortcuts import get_object_or_404
 from django.template import loader, RequestContext
diff --git a/django/contrib/flatpages/templatetags/flatpages.py b/django/contrib/flatpages/templatetags/flatpages.py
index a32ac7f..86bfc11 100644
--- a/django/contrib/flatpages/templatetags/flatpages.py
+++ b/django/contrib/flatpages/templatetags/flatpages.py
@@ -1,7 +1,7 @@
 from django import template
 from django.conf import settings
 from django.contrib.flatpages.models import FlatPage
-from django.contrib.sites.models import get_current_site
+from django.contrib.sites import get_current_site
 
 
 register = template.Library()
diff --git a/django/contrib/gis/sitemaps/views.py b/django/contrib/gis/sitemaps/views.py
index 9682c12..1857a76 100644
--- a/django/contrib/gis/sitemaps/views.py
+++ b/django/contrib/gis/sitemaps/views.py
@@ -2,7 +2,7 @@ from __future__ import unicode_literals
 
 from django.http import HttpResponse, Http404
 from django.template import loader
-from django.contrib.sites.models import get_current_site
+from django.contrib.sites import get_current_site
 from django.core import urlresolvers
 from django.core.paginator import EmptyPage, PageNotAnInteger
 from django.contrib.gis.db.models.fields import GeometryField
diff --git a/django/contrib/redirects/middleware.py b/django/contrib/redirects/middleware.py
index 03c9d97..7a57bc5 100644
--- a/django/contrib/redirects/middleware.py
+++ b/django/contrib/redirects/middleware.py
@@ -2,7 +2,7 @@ from __future__ import unicode_literals
 
 from django.conf import settings
 from django.contrib.redirects.models import Redirect
-from django.contrib.sites.models import get_current_site
+from django.contrib.sites import get_current_site
 from django.core.exceptions import ImproperlyConfigured
 from django import http
 
diff --git a/django/contrib/redirects/models.py b/django/contrib/redirects/models.py
index a0376b5..24e30ea 100644
--- a/django/contrib/redirects/models.py
+++ b/django/contrib/redirects/models.py
@@ -1,11 +1,11 @@
 from django.db import models
-from django.contrib.sites.models import Site
+from django.conf import settings
 from django.utils.translation import ugettext_lazy as _
 from django.utils.encoding import python_2_unicode_compatible
 
 @python_2_unicode_compatible
 class Redirect(models.Model):
-    site = models.ForeignKey(Site)
+    site = models.ForeignKey(settings.SITES_SITE_MODEL)
     old_path = models.CharField(_('redirect from'), max_length=200, db_index=True,
         help_text=_("This should be an absolute path, excluding the domain name. Example: '/events/search/'."))
     new_path = models.CharField(_('redirect to'), max_length=200, blank=True,
diff --git a/django/contrib/sitemaps/__init__.py b/django/contrib/sitemaps/__init__.py
index 72c1932..d9af017 100644
--- a/django/contrib/sitemaps/__init__.py
+++ b/django/contrib/sitemaps/__init__.py
@@ -1,4 +1,4 @@
-from django.contrib.sites.models import Site
+from django.contrib.sites import get_site_model
 from django.core import urlresolvers, paginator
 from django.core.exceptions import ImproperlyConfigured
 from django.utils.six.moves.urllib.parse import urlencode
@@ -30,7 +30,7 @@ def ping_google(sitemap_url=None, ping_url=PING_URL):
     if sitemap_url is None:
         raise SitemapNotFound("You didn't provide a sitemap_url, and the sitemap URL couldn't be auto-detected.")
 
-    from django.contrib.sites.models import Site
+    Site = get_site_model()
     current_site = Site.objects.get_current()
     url = "http://%s%s" % (current_site.domain, sitemap_url)
     params = urlencode({'sitemap':url})
@@ -73,6 +73,7 @@ class Sitemap(object):
 
         # Determine domain
         if site is None:
+            Site = get_site_model()
             if Site._meta.installed:
                 try:
                     site = Site.objects.get_current()
@@ -98,6 +99,7 @@ class Sitemap(object):
 
 class FlatPageSitemap(Sitemap):
     def items(self):
+        Site = get_site_model()
         current_site = Site.objects.get_current()
         return current_site.flatpage_set.filter(registration_required=False)
 
diff --git a/django/contrib/admindocs/views.py b/django/contrib/admindocs/views.py
index b3faf06..b5308ac 100644
--- a/django/contrib/admindocs/views.py
+++ b/django/contrib/admindocs/views.py
@@ -12,7 +12,6 @@ from django.core.exceptions import ImproperlyConfigured, ViewDoesNotExist
 from django.http import Http404
 from django.core import urlresolvers
 from django.contrib.admindocs import utils
-from django.contrib.sites.models import Site
 from django.utils.importlib import import_module
 from django.utils._os import upath
 from django.utils import six
@@ -125,6 +124,8 @@ def view_index(request):
     for settings_mod in settings_modules:
         urlconf = import_module(settings_mod.ROOT_URLCONF)
         view_functions = extract_views_from_urlpatterns(urlconf.urlpatterns)
+        from django.contrib.sites import get_site_model
+        Site = get_site_model()
         if Site._meta.installed:
             site_obj = Site.objects.get(pk=settings_mod.SITE_ID)
         else:
diff --git a/django/contrib/sitemaps/views.py b/django/contrib/sitemaps/views.py
index 95bc7ea..72fc1a3 100644
--- a/django/contrib/sitemaps/views.py
+++ b/django/contrib/sitemaps/views.py
@@ -1,7 +1,7 @@
 import warnings
 from functools import wraps
 
-from django.contrib.sites.models import get_current_site
+from django.contrib.sites import get_current_site
 from django.core import urlresolvers
 from django.core.paginator import EmptyPage, PageNotAnInteger
 from django.http import Http404
diff --git a/django/contrib/syndication/views.py b/django/contrib/syndication/views.py
index 3bfba3b..9537cda 100644
--- a/django/contrib/syndication/views.py
+++ b/django/contrib/syndication/views.py
@@ -3,7 +3,7 @@ from __future__ import unicode_literals
 from calendar import timegm
 
 from django.conf import settings
-from django.contrib.sites.models import get_current_site
+from django.contrib.sites import get_current_site
 from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist
 from django.http import HttpResponse, Http404
 from django.template import loader, TemplateDoesNotExist, RequestContext
diff --git a/django/contrib/sites/__init__.py b/django/contrib/sites/__init__.py
index e69de29..2474488 100644
--- a/django/contrib/sites/__init__.py
+++ b/django/contrib/sites/__init__.py
@@ -0,0 +1,32 @@
+from django.conf import settings
+from django.core.exceptions import ImproperlyConfigured
+
+
+def get_site_model():
+    """
+    Returns the Site model that is active in this project.
+    """
+    from django.db.models import get_model
+
+    try:
+        app_label, model_name = settings.SITES_SITE_MODEL.split('.')
+    except (ValueError, AttributeError):
+        raise ImproperlyConfigured("SITES_SITE_MODEL must be of the form 'app_label.model_name'")
+    site_model = get_model(app_label, model_name)
+    if site_model is None:
+        raise ImproperlyConfigured("SITES_SITE_MODEL refers to model '%s' that has not been installed" % settings.SITES_SITE_MODEL)
+    return site_model
+
+
+def get_current_site(request):
+    """
+    Checks if contrib.sites is installed and returns either the current
+    ``Site`` object or a ``RequestSite`` object based on the request.
+    """
+    from django.contrib.sites.models import RequestSite
+    Site = get_site_model()
+    if Site._meta.installed:
+        current_site = Site.objects.get_current()
+    elif RequestSite:
+        current_site = RequestSite(request)
+    return current_site
diff --git a/django/contrib/sites/models.py b/django/contrib/sites/models.py
index bbd85ed..8af74fb 100644
--- a/django/contrib/sites/models.py
+++ b/django/contrib/sites/models.py
@@ -1,12 +1,15 @@
 from __future__ import unicode_literals
 
 import string
+import warnings
 
+from django.conf import settings
 from django.db import models
 from django.db.models.signals import pre_save, pre_delete
 from django.utils.translation import ugettext_lazy as _
 from django.utils.encoding import python_2_unicode_compatible
 from django.core.exceptions import ValidationError
+from django.contrib import sites
 
 
 SITE_CACHE = {}
@@ -35,7 +38,6 @@ class SiteManager(models.Manager):
         project's settings. The ``Site`` object is cached the first
         time it's retrieved from the database.
         """
-        from django.conf import settings
         try:
             sid = settings.SITE_ID
         except AttributeError:
@@ -55,14 +57,14 @@ class SiteManager(models.Manager):
 
 
 @python_2_unicode_compatible
-class Site(models.Model):
-
-    domain = models.CharField(_('domain name'), max_length=100,
+class AbstractSite(models.Model):
+    domain = models.CharField(_('domain name'), max_length=100, db_index=True,
         validators=[_simple_domain_name_validator])
-    name = models.CharField(_('display name'), max_length=50)
+
     objects = SiteManager()
 
     class Meta:
+        abstract = True
         db_table = 'django_site'
         verbose_name = _('site')
         verbose_name_plural = _('sites')
@@ -72,6 +74,13 @@ class Site(models.Model):
         return self.domain
 
 
+class Site(AbstractSite):
+    name = models.CharField(_('display name'), max_length=50)
+
+    class Meta(AbstractSite.Meta):
+        swappable = 'SITES_SITE_MODEL'
+
+
 @python_2_unicode_compatible
 class RequestSite(object):
     """
@@ -95,15 +104,8 @@ class RequestSite(object):
 
 
 def get_current_site(request):
-    """
-    Checks if contrib.sites is installed and returns either the current
-    ``Site`` object or a ``RequestSite`` object based on the request.
-    """
-    if Site._meta.installed:
-        current_site = Site.objects.get_current()
-    else:
-        current_site = RequestSite(request)
-    return current_site
+    warnings.warn("django.contrib.sites.models.get_current_site is deprecated, use django.contrib.sites.get_current_site instead.", PendingDeprecationWarning)
+    return sites.get_current_site(request)
 
 
 def clear_site_cache(sender, **kwargs):
@@ -115,5 +117,5 @@ def clear_site_cache(sender, **kwargs):
         del SITE_CACHE[instance.pk]
     except KeyError:
         pass
-pre_save.connect(clear_site_cache, sender=Site)
-pre_delete.connect(clear_site_cache, sender=Site)
+pre_save.connect(clear_site_cache, sender=settings.SITES_SITE_MODEL)
+pre_delete.connect(clear_site_cache, sender=settings.SITES_SITE_MODEL)
diff --git a/django/contrib/contenttypes/tests.py b/django/contrib/contenttypes/tests.py
index f300294..dc12aee 100644
--- a/django/contrib/contenttypes/tests.py
+++ b/django/contrib/contenttypes/tests.py
@@ -3,7 +3,7 @@ from __future__ import unicode_literals
 from django.db import models
 from django.contrib.contenttypes.models import ContentType
 from django.contrib.contenttypes.views import shortcut
-from django.contrib.sites.models import Site, get_current_site
+from django.contrib.sites import get_site_model, get_current_site
 from django.http import HttpRequest, Http404
 from django.test import TestCase
 from django.test.utils import override_settings
@@ -11,6 +11,8 @@ from django.utils.http import urlquote
 from django.utils import six
 from django.utils.encoding import python_2_unicode_compatible
 
+Site = get_site_model()
+
 
 class ConcreteModel(models.Model):
     name = models.CharField(max_length=10)
diff --git a/django/contrib/sites/tests.py b/django/contrib/sites/tests.py
index 6bfbfd7..89d8f46 100644
--- a/django/contrib/sites/tests.py
+++ b/django/contrib/sites/tests.py
@@ -1,12 +1,15 @@
 from __future__ import unicode_literals
 
 from django.conf import settings
-from django.contrib.sites.models import Site, RequestSite, get_current_site
+from django.contrib.sites import get_site_model, get_current_site
+from django.contrib.sites.models import RequestSite
 from django.core.exceptions import ObjectDoesNotExist, ValidationError
 from django.http import HttpRequest
 from django.test import TestCase
 from django.test.utils import override_settings
 
+Site = get_site_model()
+
 
 class SitesFrameworkTests(TestCase):
 
