diff --git a/server/bindata.go b/server/bindata.go
index d7a1cd7a401b6e69ade1a7ce2f22c69dc9c9ab54..2ca49775e0bdf1364d49db148c642e62aa85af1f 100644
--- a/server/bindata.go
+++ b/server/bindata.go
@@ -221,7 +221,7 @@ idlogout.get_services = function() {
     return JSON.parse($('#services').attr('data-services'));
 };
 
-idlogout.logout_service = function(idx, service) {
+idlogout.logout_service = function(idx, service, successCallback, errorCallback) {
     console.log('logging out of ' + service.name);
     $.ajax({
         type: 'GET',
@@ -233,24 +233,47 @@ idlogout.logout_service = function(idx, service) {
         success: function() {
             $('#status_'+idx).addClass('logout-status-ok').text('OK');
             console.log('successful logout for ' + service.name);
+            successCallback(service);
         },
         error: function() {
             $('#status_'+idx).addClass('logout-status-error').text('ERROR');
             console.log('error logging out of ' + service.name);
+            errorCallback(service);
         }
     });
 };
 
-idlogout.logout = function() {
+idlogout.logout = function(doneCallback) {
     var services = idlogout.get_services();
+    var remaining = services.length;
+    var ok = 0, errors = 0;
+    var maybeDone = function() {
+        remaining--;
+        if (remaining <= 0) {
+            doneCallback(ok, errors);
+        }
+    };
     $.each(services, function(index, arg) {
-        idlogout.logout_service(index, arg);
+        idlogout.logout_service(index, arg, function(svc) {
+            ok++;
+            maybeDone();
+        }, function(svc) {
+            errors++;
+            maybeDone();
+        });
     });
 };
 
 $(function() {
     $('.logout-status').show();
-    idlogout.logout();
+    idlogout.logout(function(ok, errors) {
+        if (errors > 0) {
+            console.log('there were errors in the logout process, we have reached an unsafe state');
+            $('#logout_err').show();
+        } else {
+            $('#logout_ok').show();
+        }
+    });
 });
 `)
 
@@ -264,7 +287,7 @@ func staticJsLogoutJs() (*asset, error) {
 		return nil, err
 	}
 
-	info := bindataFileInfo{name: "static/js/logout.js", size: 1005, mode: os.FileMode(420), modTime: time.Unix(1535013418, 0)}
+	info := bindataFileInfo{name: "static/js/logout.js", size: 1726, mode: os.FileMode(420), modTime: time.Unix(1581161609, 0)}
 	a := &asset{bytes: bytes, info: info}
 	return a, nil
 }
@@ -1380,7 +1403,17 @@ var _templatesLogoutHtml = []byte(`{{define "title"}}Sign Out{{end}}
           {{end}}
       </tbody></table>
 
-      <div id="services" data-services="{{.ServicesJSON}}"></div>
+      <div id="services" data-services="{{json .Services}}"></div>
+
+      <p id="logout_ok" class="hidden">
+        You have been successfully logged out from all services.
+      </p>
+
+      <p id="logout_err" class="hidden">
+        There were some errors in the logout process.
+        <b>You must quit the browser</b> to avoid leaving open
+        sessions around!
+      </p>
 
     </div>
 
@@ -1403,7 +1436,7 @@ func templatesLogoutHtml() (*asset, error) {
 		return nil, err
 	}
 
-	info := bindataFileInfo{name: "templates/logout.html", size: 1192, mode: os.FileMode(420), modTime: time.Unix(1581160289, 0)}
+	info := bindataFileInfo{name: "templates/logout.html", size: 1505, mode: os.FileMode(420), modTime: time.Unix(1581161411, 0)}
 	a := &asset{bytes: bytes, info: info}
 	return a, nil
 }
diff --git a/server/http.go b/server/http.go
index 53334e72b671fe6ecd2a5e21ba11120ee338af99..1e156c21c9f8cc9e0abea82deed13379c1c70e96 100644
--- a/server/http.go
+++ b/server/http.go
@@ -357,12 +357,8 @@ func (h *Server) handleLogout(w http.ResponseWriter, req *http.Request) {
 			URL:  serviceLogoutCallback(svc),
 		})
 	}
-
-	svcJSON, _ := json.Marshal(svcs) // nolint
 	data := map[string]interface{}{
-		"Services":             svcs,
-		"ServicesJSON":         string(svcJSON),
-		"IncludeLogoutScripts": true,
+		"Services": svcs,
 	}
 
 	// Close the keystore.
diff --git a/server/http_test.go b/server/http_test.go
index 64e266e1c535ae40ba1c666edacbcf9c57428b72..3c932a5e0bd0c3a4aa3fced5a1823e5d60567023 100644
--- a/server/http_test.go
+++ b/server/http_test.go
@@ -7,6 +7,7 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
+	"html"
 	"io"
 	"io/ioutil"
 	"net"
@@ -271,6 +272,8 @@ func checkAuthFailure(t testing.TB, resp *http.Response) {
 	}
 }
 
+var logoutServicesRx = regexp.MustCompile(`<div id="services" data-services="([^"]*)">`)
+
 func checkLogoutPage(t testing.TB, resp *http.Response) {
 	if resp.Request.URL.Path != "/logout" {
 		t.Errorf("request path is not /logout (%s)", resp.Request.URL.String())
@@ -281,9 +284,30 @@ func checkLogoutPage(t testing.TB, resp *http.Response) {
 		t.Fatalf("reading body: %v", err)
 	}
 
-	if s := string(data); !strings.Contains(s, "Signing you out from all services") {
+	s := string(data)
+	// Check signature string of logout page.
+	if !strings.Contains(s, "Signing you out from all services") {
 		t.Fatalf("not the logout page:\n%s", s)
 	}
+	// Check presence of fallback service logout URL.
+	if !strings.Contains(s, "<img src=\"https://service.example.com/sso_logout\"") {
+		t.Fatalf("logout page does not contain fallback service logout URL:\n%s", s)
+	}
+	// Parse the JSON in the services div.
+	m := logoutServicesRx.FindStringSubmatch(s)
+	if len(m) == 0 {
+		t.Fatalf("logout page does not contain JSON-encoded services:\n%s", s)
+	}
+	var svcs []logoutServiceInfo
+	if err := json.Unmarshal([]byte(html.UnescapeString(m[1])), &svcs); err != nil {
+		t.Fatalf("error decoding JSON services: %v", err)
+	}
+	if len(svcs) != 1 {
+		t.Fatalf("expected 1 service, got %d: %v", len(svcs), svcs)
+	}
+	if svcs[0].URL != "https://service.example.com/sso_logout" {
+		t.Fatalf("bad service logout URL: %s", svcs[0].URL)
+	}
 }
 
 func extractSSOTicket(dest *string) func(testing.TB, *http.Response) {
diff --git a/server/sri_map.go b/server/sri_map.go
index 5e43bc574dbc7d0ecf07f025117e115d5efa8bd7..4149e6974b89a16461ac498a3de830ed98a46650 100644
--- a/server/sri_map.go
+++ b/server/sri_map.go
@@ -5,7 +5,7 @@ var sriMap = map[string]string{
 	"/static/css/signin.css": "sha384-Eg6R7EvngjwfifE75qnyhwjgbAuSpVLDEWoYuNWpF2Yn7QsqKCvxpFsR5YCrzV5d",
 	"/static/js/bootstrap-4.1.3.min.js": "sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy",
 	"/static/js/jquery-3.3.1.min.js": "sha384-tsQFqpEReu7ZLhBV2VZlAu7zcOV+rXbYlF2cqB8txI/8aZajjp4Bqd+V6D5IgvKT",
-	"/static/js/logout.js": "sha384-lChVngGLNFXetIJTSxc+scDpi1vsBL+7Xa4r2uZpQFP/6Y2z9eCDXe/Y4IUdklRD",
+	"/static/js/logout.js": "sha384-XfIFkQSluN5TZzrogxlZGauRX7e9Do42s6Y/Cx6RX9qcKtgw0FGSbEpPbCqQbFnb",
 	"/static/js/popper-1.14.3.min.js": "sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49",
 	"/static/js/u2f-api.js": "sha384-9ChevE6pp8ArGK03HgolnFjZbF3webZQtYkwcabzbcI28Lx1/2x2j2fbaAWD4cgR",
 	"/static/js/u2f.js": "sha384-7zZy25ajTABErGlCQgcyRDpQDS9QVZv9o+95IfvCjWftQe20f411F1a39Ge5xmCe",
diff --git a/server/static/js/logout.js b/server/static/js/logout.js
index b3b8a4a795332c4ef4ac6c6bb662585889c226eb..e4654871be4e615ba25ab8a300db992ab0dbc3ee 100644
--- a/server/static/js/logout.js
+++ b/server/static/js/logout.js
@@ -4,7 +4,7 @@ idlogout.get_services = function() {
     return JSON.parse($('#services').attr('data-services'));
 };
 
-idlogout.logout_service = function(idx, service) {
+idlogout.logout_service = function(idx, service, successCallback, errorCallback) {
     console.log('logging out of ' + service.name);
     $.ajax({
         type: 'GET',
@@ -16,22 +16,45 @@ idlogout.logout_service = function(idx, service) {
         success: function() {
             $('#status_'+idx).addClass('logout-status-ok').text('OK');
             console.log('successful logout for ' + service.name);
+            successCallback(service);
         },
         error: function() {
             $('#status_'+idx).addClass('logout-status-error').text('ERROR');
             console.log('error logging out of ' + service.name);
+            errorCallback(service);
         }
     });
 };
 
-idlogout.logout = function() {
+idlogout.logout = function(doneCallback) {
     var services = idlogout.get_services();
+    var remaining = services.length;
+    var ok = 0, errors = 0;
+    var maybeDone = function() {
+        remaining--;
+        if (remaining <= 0) {
+            doneCallback(ok, errors);
+        }
+    };
     $.each(services, function(index, arg) {
-        idlogout.logout_service(index, arg);
+        idlogout.logout_service(index, arg, function(svc) {
+            ok++;
+            maybeDone();
+        }, function(svc) {
+            errors++;
+            maybeDone();
+        });
     });
 };
 
 $(function() {
     $('.logout-status').show();
-    idlogout.logout();
+    idlogout.logout(function(ok, errors) {
+        if (errors > 0) {
+            console.log('there were errors in the logout process, we have reached an unsafe state');
+            $('#logout_err').show();
+        } else {
+            $('#logout_ok').show();
+        }
+    });
 });
diff --git a/server/templates/logout.html b/server/templates/logout.html
index 066e40e14b0901b9129373c261fea9f04a7000a3..3af52f2d2ae7c3bc9cc8c2896a7824f2b59d687a 100644
--- a/server/templates/logout.html
+++ b/server/templates/logout.html
@@ -38,7 +38,17 @@
           {{end}}
       </tbody></table>
 
-      <div id="services" data-services="{{.ServicesJSON}}"></div>
+      <div id="services" data-services="{{json .Services}}"></div>
+
+      <p id="logout_ok" class="hidden">
+        You have been successfully logged out from all services.
+      </p>
+
+      <p id="logout_err" class="hidden">
+        There were some errors in the logout process.
+        <b>You must quit the browser</b> to avoid leaving open
+        sessions around!
+      </p>
 
     </div>