diff --git a/django/contrib/auth/admin.py b/django/contrib/auth/admin.py
index 928236914a..83aa5b19ed 100644
--- a/django/contrib/auth/admin.py
+++ b/django/contrib/auth/admin.py
@@ -2,6 +2,7 @@ from django.conf import settings
 from django.conf.urls import url
 from django.contrib import admin, messages
 from django.contrib.admin.options import IS_POPUP_VAR
+from django.contrib.admin.utils import unquote
 from django.contrib.auth import update_session_auth_hash
 from django.contrib.auth.forms import (
     AdminPasswordChangeForm, UserChangeForm, UserCreationForm,
@@ -10,9 +11,9 @@ from django.contrib.auth.models import Group, User
 from django.core.exceptions import PermissionDenied
 from django.db import transaction
 from django.http import Http404, HttpResponseRedirect
-from django.shortcuts import get_object_or_404
 from django.template.response import TemplateResponse
 from django.utils.decorators import method_decorator
+from django.utils.encoding import force_text
 from django.utils.html import escape
 from django.utils.translation import ugettext, ugettext_lazy as _
 from django.views.decorators.csrf import csrf_protect
@@ -79,7 +80,7 @@ class UserAdmin(admin.ModelAdmin):
 
     def get_urls(self):
         return [
-            url(r'^(\d+)/password/$', self.admin_site.admin_view(self.user_change_password), name='auth_user_password_change'),
+            url(r'^(.+)/password/$', self.admin_site.admin_view(self.user_change_password), name='auth_user_password_change'),
         ] + super(UserAdmin, self).get_urls()
 
     def lookup_allowed(self, lookup, value):
@@ -123,7 +124,12 @@ class UserAdmin(admin.ModelAdmin):
     def user_change_password(self, request, id, form_url=''):
         if not self.has_change_permission(request):
             raise PermissionDenied
-        user = get_object_or_404(self.get_queryset(request), pk=id)
+        user = self.get_object(request, unquote(id))
+        if user is None:
+            raise Http404(_('%(name)s object with primary key %(key)r does not exist.') % {
+                'name': force_text(self.model._meta.verbose_name),
+                'key': escape(id),
+            })
         if request.method == 'POST':
             form = self.change_password_form(user, request.POST)
             if form.is_valid():
diff --git a/tests/auth_tests/test_views.py b/tests/auth_tests/test_views.py
index 42ebb97d73..8f0bf59fff 100644
--- a/tests/auth_tests/test_views.py
+++ b/tests/auth_tests/test_views.py
@@ -18,6 +18,7 @@ from django.contrib.sessions.middleware import SessionMiddleware
 from django.contrib.sites.requests import RequestSite
 from django.core import mail
 from django.core.urlresolvers import NoReverseMatch, reverse, reverse_lazy
+from django.db import connection
 from django.http import HttpRequest, QueryDict
 from django.middleware.csrf import CsrfViewMiddleware
 from django.test import (
@@ -30,6 +31,7 @@ from django.utils.http import urlquote
 from django.utils.six.moves.urllib.parse import ParseResult, urlparse
 from django.utils.translation import LANGUAGE_SESSION_KEY
 
+from .models import UUIDUser
 from .settings import AUTH_TEMPLATES
 
 
@@ -892,3 +894,38 @@ class ChangelistTests(AuthViewsTestCase):
         self.assertEqual(row.user_id, self.admin.pk)
         self.assertEqual(row.object_id, str(u.pk))
         self.assertEqual(row.change_message, 'Changed password.')
+
+    def test_password_change_bad_url(self):
+        response = self.client.get(reverse('auth_test_admin:auth_user_password_change', args=('foobar',)))
+        self.assertEqual(response.status_code, 404)
+
+
+@override_settings(
+    AUTH_USER_MODEL='auth.UUIDUser',
+    ROOT_URLCONF='auth_tests.urls_custom_user_admin',
+)
+class UUIDUserTests(TestCase):
+
+    def test_admin_password_change(self):
+        u = UUIDUser.objects.create_superuser(username='uuid', email='foo@bar.com', password='test')
+        self.assertTrue(self.client.login(username='uuid', password='test'))
+
+        user_change_url = reverse('custom_user_admin:auth_uuiduser_change', args=(u.pk,))
+        response = self.client.get(user_change_url)
+        self.assertEqual(response.status_code, 200)
+
+        password_change_url = reverse('custom_user_admin:auth_user_password_change', args=(u.pk,))
+        response = self.client.get(password_change_url)
+        self.assertEqual(response.status_code, 200)
+
+        # A LogEntry is created with pk=1 which breaks a FK constraint on MySQL
+        with connection.constraint_checks_disabled():
+            response = self.client.post(password_change_url, {
+                'password1': 'password1',
+                'password2': 'password1',
+            })
+        self.assertRedirects(response, user_change_url)
+        row = LogEntry.objects.latest('id')
+        self.assertEqual(row.user_id, 1)  # harcoded in CustomUserAdmin.log_change()
+        self.assertEqual(row.object_id, str(u.pk))
+        self.assertEqual(row.change_message, 'Changed password.')
diff --git a/tests/auth_tests/urls_custom_user_admin.py b/tests/auth_tests/urls_custom_user_admin.py
new file mode 100644
index 0000000000..02389de4f2
--- /dev/null
+++ b/tests/auth_tests/urls_custom_user_admin.py
@@ -0,0 +1,21 @@
+from django.conf.urls import include, url
+from django.contrib import admin
+from django.contrib.auth import get_user_model
+from django.contrib.auth.admin import UserAdmin
+from django.contrib.auth.urls import urlpatterns
+
+site = admin.AdminSite(name='custom_user_admin')
+
+
+class CustomUserAdmin(UserAdmin):
+    def log_change(self, request, object, message):
+        # LogEntry.user column doesn't get altered to expect a UUID, so set an
+        # integer manually to avoid causing an error.
+        request.user.pk = 1
+        super(CustomUserAdmin, self).log_change(request, object, message)
+
+site.register(get_user_model(), CustomUserAdmin)
+
+urlpatterns += [
+    url(r'^admin/', include(site.urls)),
+]