diff --git a/server/http.go b/server/http.go
index a277bf945b6672e6d6dc79e38b4e6890ad1dce7c..52bdbe369d9bc254087fc6c846eeb95491fb5b64 100644
--- a/server/http.go
+++ b/server/http.go
@@ -295,30 +295,47 @@ func (h *Server) handleExchange(w http.ResponseWriter, req *http.Request) {
 
 // Handler returns the http.Handler for the SSO server application.
 func (h *Server) Handler() http.Handler {
-	m := mux.NewRouter()
+	// The root HTTP handler. This must be a gorilla/mux.Router since
+	// sessions depend on it.
+	root := mux.NewRouter()
 
-	var lih, loh http.Handler
-	lih = h.loginHandler
-	loh = h.withAuth(h.handleLogout)
-	if h.csrfSecret != nil {
-		csrfW := csrf.Protect(h.csrfSecret)
-		lih = csrfW(lih)
-		loh = csrfW(loh)
-	}
-	m.Handle("/login", withDynamicHeaders(lih))
-	m.Handle("/logout", withDynamicHeaders(loh))
-
-	m.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(&assetfs.AssetFS{
+	// Serve static content to anyone.
+	root.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(&assetfs.AssetFS{
 		Asset:     Asset,
 		AssetDir:  AssetDir,
 		AssetInfo: AssetInfo,
 		Prefix:    "static",
 	})))
 
-	m.Handle("/exchange", withDynamicHeaders(http.HandlerFunc(h.handleExchange)))
-	m.Handle("/", withDynamicHeaders(h.withAuth(h.handleHomepage)))
+	// Build the main IDP application router, with optional CSRF
+	// protection.
+	m := http.NewServeMux()
+	m.Handle("/login", h.loginHandler)
+	m.Handle("/logout", h.withAuth(h.handleLogout))
+	idph := http.Handler(m)
+	if h.csrfSecret != nil {
+		idph = csrf.Protect(h.csrfSecret)(idph)
+	}
+
+	// Add the SSO provider endpoints (root path and /exchange),
+	// which do not need CSRF. We use a HandlerFunc to bypass the
+	// '/' dispatch semantics of the standard http.ServeMux.
+	ssoh := h.withAuth(h.handleHomepage)
+	userh := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		switch {
+		case r.Method == "GET" && r.URL.Path == "/":
+			ssoh.ServeHTTP(w, r)
+		case r.URL.Path == "/exchange":
+			h.handleExchange(w, r)
+		default:
+			idph.ServeHTTP(w, r)
+		}
+	})
 
-	return m
+	// User-facing routes require cache-busting and CSP headers.
+	root.PathPrefix("/").Handler(withDynamicHeaders(userh))
+
+	return root
 }
 
 // A relatively strict CSP.