Index: django/contrib/sites/managers.py
===================================================================
--- django/contrib/sites/managers.py	(revision 15231)
+++ django/contrib/sites/managers.py	(working copy)
@@ -1,6 +1,6 @@
-from django.conf import settings
 from django.db import models
 from django.db.models.fields import FieldDoesNotExist
+from django.contrib.sites.models import Site
 
 class CurrentSiteManager(models.Manager):
     "Use this to limit objects to those associated with the current site."
@@ -38,4 +38,6 @@
     def get_query_set(self):
         if not self.__is_validated:
             self._validate_field_name()
-        return super(CurrentSiteManager, self).get_query_set().filter(**{self.__field_name + '__id__exact': settings.SITE_ID})
+        return super(CurrentSiteManager, self).get_query_set().filter(
+          **{self.__field_name: Site.objects.get_current()}
+        )
Index: django/contrib/sites/middleware.py
===================================================================
--- django/contrib/sites/middleware.py	(revision 0)
+++ django/contrib/sites/middleware.py	(revision 0)
@@ -0,0 +1,38 @@
+from django.utils.cache import patch_vary_headers
+from django.contrib.sites.models import get_current_site, Site, RequestSite, SITE_CACHE
+
+
+class LazySite(object):
+    """
+    A lazy site object that refers to either Site instance or
+    a case insensitive RequestSite.
+    """
+    def get_site(self, request):
+        get_current_site(request)
+        return site
+
+    def __get__(self, request, obj_type=None):
+        if not hasattr(request, '_cached_site'):
+            request._cached_site = self.get_site(request)
+        return request._cached_site
+
+
+class SitesMiddleware(object):
+
+    def process_request(self, request):
+        if not hasattr(request.__class__, 'site'):
+            request.__class__.site = LazySite()
+        return None
+
+    def process_response(self, request, response):
+        """
+        Forces the HTTP Vary header onto requests to avoid having responses
+        cached from incorrect urlconfs.
+
+        If you'd like to disable this for some reason, set `FORCE_VARY_ON_HOST`
+        in your Django settings file to `False`.
+        """
+        if getattr(settings, 'SITES_VARY_ON_HOST', True):
+            patch_vary_headers(response, ('Host',))
+        return response
+
Index: django/contrib/sites/tests.py
===================================================================
--- django/contrib/sites/tests.py	(revision 15231)
+++ django/contrib/sites/tests.py	(working copy)
@@ -8,7 +8,8 @@
 class SitesFrameworkTests(TestCase):
 
     def setUp(self):
-        Site(id=settings.SITE_ID, domain="example.com", name="example.com").save()
+        Site(id=settings.SITE_ID, domain="staticsite.com", name="staticsite.com").save()
+        Site(domain="dynamicsite.com", name="dynamicsite.com").save()
         self.old_Site_meta_installed = Site._meta.installed
         Site._meta.installed = True
 
@@ -26,7 +27,7 @@
         # After updating a Site object (e.g. via the admin), we shouldn't return a
         # bogus value from the SITE_CACHE.
         site = Site.objects.get_current()
-        self.assertEqual(u"example.com", site.name)
+        self.assertEqual(u"staticsite.com", site.name)
         s2 = Site.objects.get(id=settings.SITE_ID)
         s2.name = "Example site"
         s2.save()
@@ -37,7 +38,7 @@
         # Test that the correct Site object is returned
         request = HttpRequest()
         request.META = {
-            "SERVER_NAME": "example.com",
+            "SERVER_NAME": "staticsite.com",
             "SERVER_PORT": "80",
         }
         site = get_current_site(request)
@@ -49,8 +50,24 @@
         site.delete()
         self.assertRaises(ObjectDoesNotExist, get_current_site, request)
 
+        request = HttpRequest()
+        request.META = {
+            "SERVER_NAME": "dynamicsite.com",
+            "SERVER_PORT": "80",
+        }
+        site = get_current_site(request)
+        self.assert_(isinstance(site, Site))
+        self.assertNotEqual(site.id, settings.SITE_ID)
+        self.assertEqual(site.domain, "dynamicsite.com")
+
+        # Test that an exception is raised if the sites framework is installed
+        # but there is no matching Site
+        site.delete()
+        self.assertRaises(ObjectDoesNotExist, get_current_site, request)
+
         # A RequestSite is returned if the sites framework is not installed
         Site._meta.installed = False
         site = get_current_site(request)
         self.assert_(isinstance(site, RequestSite))
-        self.assertEqual(site.name, u"example.com")
+        self.assertEqual(site.name, u"dynamicsite.com")
+
Index: django/contrib/sites/models.py
===================================================================
--- django/contrib/sites/models.py	(revision 15231)
+++ django/contrib/sites/models.py	(working copy)
@@ -1,5 +1,6 @@
 from django.db import models
 from django.utils.translation import ugettext_lazy as _
+from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist
 
 
 SITE_CACHE = {}
@@ -7,24 +8,41 @@
 
 class SiteManager(models.Manager):
 
+    def get_site_by_request(self, request):
+        host = request.get_host().lower()
+        if ':' in host:
+            host, _ = host.split(':', 1)
+        return self.get(domain__iexact=host)
+
+    def get_site_by_id(self, id):
+        site = SITE_CACHE.get(id, None)
+        if site is None:
+            site = self.get(pk=id)
+            SITE_CACHE[id] = site
+        return site
+
     def get_current(self):
         """
         Returns the current ``Site`` based on the SITE_ID in the
         project's settings. The ``Site`` object is cached the first
         time it's retrieved from the database.
         """
+        from warnings import warn
+        warn(
+            "The SiteManager.get_current() method is deprecated in favor of the "
+            "get_current_site() function.", DeprecationWarning
+        )
+
         from django.conf import settings
         try:
             sid = settings.SITE_ID
         except AttributeError:
-            from django.core.exceptions import ImproperlyConfigured
-            raise ImproperlyConfigured("You're using the Django \"sites framework\" without having set the SITE_ID setting. Create a site in your database and set the SITE_ID setting to fix this error.")
-        try:
-            current_site = SITE_CACHE[sid]
-        except KeyError:
-            current_site = self.get(pk=sid)
-            SITE_CACHE[sid] = current_site
-        return current_site
+            raise ImproperlyConfigured(
+                "You're using the Django \"sites framework\" without having set "
+                "the SITE_ID setting. Create a site in your database and set "
+                "the SITE_ID setting to fix this error."
+            )
+        return self.get_site_by_id(sid)
 
     def clear_cache(self):
         """Clears the ``Site`` object cache."""
@@ -34,7 +52,7 @@
 
 class Site(models.Model):
 
-    domain = models.CharField(_('domain name'), max_length=100)
+    domain = models.CharField(_('domain name'), max_length=100, unique=True)
     name = models.CharField(_('display name'), max_length=50)
     objects = SiteManager()
 
@@ -71,7 +89,7 @@
     The save() and delete() methods raise NotImplementedError.
     """
     def __init__(self, request):
-        self.domain = self.name = request.get_host()
+        self.domain = self.name = request.get_host().lower()
 
     def __unicode__(self):
         return self.domain
@@ -83,13 +101,35 @@
         raise NotImplementedError('RequestSite cannot be deleted.')
 
 
-def get_current_site(request):
+def get_current_site(request, require_orm_object=False):
     """
     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()
+    from django.conf import settings
+    if hasattr(settings, 'SITE_CALLBACK'):
+        if callable(settings.SITE_CALLBACK):
+            return settings.SITE_CALLBACK(request, require_orm_object)
+        else:
+            raise ImproperlyConfigured(
+                'Settings contains a SITE_CALLBACK value which is not callable.'
+            )
+
+    elif not Site._meta.installed:
+        if not require_orm_object:
+            return RequestSite(request)
+        else:
+            raise ImproperlyConfigured(
+                'The Django "sites framework" is being used somewhere in '
+                'your project in a manner that requires it to be added '
+                'to INSTALLED_APPS in your settings file.'
+            )
+
     else:
-        current_site = RequestSite(request)
-    return current_site
+        try:
+            return Site.objects.get_site_by_request(request)
+
+        except ObjectDoesNotExist:
+            return Site.objects.get_site_by_id(settings.SITE_ID)
+        
+
Index: django/contrib/sites/decorators.py
===================================================================
--- django/contrib/sites/decorators.py	(revision 0)
+++ django/contrib/sites/decorators.py	(revision 0)
@@ -0,0 +1,11 @@
+from django.utils.decorators import decorator_from_middleware
+
+from django.contrib.sites.middleware import SitesMiddleware
+
+site_aware = decorator_from_middleware(SitesMiddleware)
+site_aware.__name__ = "site_aware"
+site_aware.__doc__ = """
+This decorator adds a LazySite instance to the request in exactly the same
+way as the SitesMiddleware, but it can be used on a per view basis.
+"""
+
