diff --git a/.gitignore b/.gitignore
index db3eaf3e3a6b0c62e1796105ed695ff226c8784a..21ad3ba82f809e9833f5fef76acbc3aa7540e8ea 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
 bin
 dist
 _output
+.idea
diff --git a/server/handlers.go b/server/handlers.go
index 9bff36ee84cd75a034dadaa505107419e1fa83e5..1391e58997d52348b7c174bac467142cdde7cefb 100644
--- a/server/handlers.go
+++ b/server/handlers.go
@@ -101,7 +101,7 @@ func (h *healthChecker) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	h.mu.RUnlock()
 
 	if err != nil {
-		h.s.renderError(w, http.StatusInternalServerError, "Health check failed.")
+		h.s.renderError(r, w, http.StatusInternalServerError, "Health check failed.")
 		return
 	}
 	fmt.Fprintf(w, "Health check passed in %s", t)
@@ -112,13 +112,13 @@ func (s *Server) handlePublicKeys(w http.ResponseWriter, r *http.Request) {
 	keys, err := s.storage.GetKeys()
 	if err != nil {
 		s.logger.Errorf("failed to get keys: %v", err)
-		s.renderError(w, http.StatusInternalServerError, "Internal server error.")
+		s.renderError(r, w, http.StatusInternalServerError, "Internal server error.")
 		return
 	}
 
 	if keys.SigningKeyPub == nil {
 		s.logger.Errorf("No public keys found.")
-		s.renderError(w, http.StatusInternalServerError, "Internal server error.")
+		s.renderError(r, w, http.StatusInternalServerError, "Internal server error.")
 		return
 	}
 
@@ -133,7 +133,7 @@ func (s *Server) handlePublicKeys(w http.ResponseWriter, r *http.Request) {
 	data, err := json.MarshalIndent(jwks, "", "  ")
 	if err != nil {
 		s.logger.Errorf("failed to marshal discovery data: %v", err)
-		s.renderError(w, http.StatusInternalServerError, "Internal server error.")
+		s.renderError(r, w, http.StatusInternalServerError, "Internal server error.")
 		return
 	}
 	maxAge := keys.NextRotation.Sub(s.now())
@@ -214,7 +214,7 @@ func (s *Server) handleAuthorization(w http.ResponseWriter, r *http.Request) {
 			status = err.Status()
 		}
 
-		s.renderError(w, status, err.Error())
+		s.renderError(r, w, status, err.Error())
 		return
 	}
 
@@ -226,14 +226,14 @@ func (s *Server) handleAuthorization(w http.ResponseWriter, r *http.Request) {
 	authReq.Expiry = s.now().Add(s.authRequestsValidFor)
 	if err := s.storage.CreateAuthRequest(*authReq); err != nil {
 		s.logger.Errorf("Failed to create authorization request: %v", err)
-		s.renderError(w, http.StatusInternalServerError, "Failed to connect to the database.")
+		s.renderError(r, w, http.StatusInternalServerError, "Failed to connect to the database.")
 		return
 	}
 
 	connectors, err := s.storage.ListConnectors()
 	if err != nil {
 		s.logger.Errorf("Failed to get list of connectors: %v", err)
-		s.renderError(w, http.StatusInternalServerError, "Failed to retrieve connector list.")
+		s.renderError(r, w, http.StatusInternalServerError, "Failed to retrieve connector list.")
 		return
 	}
 
@@ -271,7 +271,7 @@ func (s *Server) handleAuthorization(w http.ResponseWriter, r *http.Request) {
 		i++
 	}
 
-	if err := s.templates.login(w, connectorInfos); err != nil {
+	if err := s.templates.login(r, w, connectorInfos, r.URL.Path); err != nil {
 		s.logger.Errorf("Server template error: %v", err)
 	}
 }
@@ -281,7 +281,7 @@ func (s *Server) handleConnectorLogin(w http.ResponseWriter, r *http.Request) {
 	conn, err := s.getConnector(connID)
 	if err != nil {
 		s.logger.Errorf("Failed to create authorization request: %v", err)
-		s.renderError(w, http.StatusBadRequest, "Requested resource does not exist")
+		s.renderError(r, w, http.StatusBadRequest, "Requested resource does not exist")
 		return
 	}
 
@@ -291,9 +291,9 @@ func (s *Server) handleConnectorLogin(w http.ResponseWriter, r *http.Request) {
 	if err != nil {
 		s.logger.Errorf("Failed to get auth request: %v", err)
 		if err == storage.ErrNotFound {
-			s.renderError(w, http.StatusBadRequest, "Login session expired.")
+			s.renderError(r, w, http.StatusBadRequest, "Login session expired.")
 		} else {
-			s.renderError(w, http.StatusInternalServerError, "Database error.")
+			s.renderError(r, w, http.StatusInternalServerError, "Database error.")
 		}
 		return
 	}
@@ -306,7 +306,7 @@ func (s *Server) handleConnectorLogin(w http.ResponseWriter, r *http.Request) {
 		}
 		if err := s.storage.UpdateAuthRequest(authReqID, updater); err != nil {
 			s.logger.Errorf("Failed to set connector ID on auth request: %v", err)
-			s.renderError(w, http.StatusInternalServerError, "Database error.")
+			s.renderError(r, w, http.StatusInternalServerError, "Database error.")
 			return
 		}
 	}
@@ -324,19 +324,19 @@ func (s *Server) handleConnectorLogin(w http.ResponseWriter, r *http.Request) {
 			callbackURL, err := conn.LoginURL(scopes, s.absURL("/callback"), authReqID)
 			if err != nil {
 				s.logger.Errorf("Connector %q returned error when creating callback: %v", connID, err)
-				s.renderError(w, http.StatusInternalServerError, "Login error.")
+				s.renderError(r, w, http.StatusInternalServerError, "Login error.")
 				return
 			}
 			http.Redirect(w, r, callbackURL, http.StatusFound)
 		case connector.PasswordConnector:
-			if err := s.templates.password(w, r.URL.String(), "", usernamePrompt(conn), false, showBacklink); err != nil {
+			if err := s.templates.password(r, w, r.URL.String(), "", usernamePrompt(conn), false, showBacklink, r.URL.Path); err != nil {
 				s.logger.Errorf("Server template error: %v", err)
 			}
 		case connector.SAMLConnector:
 			action, value, err := conn.POSTData(scopes, authReqID)
 			if err != nil {
 				s.logger.Errorf("Creating SAML data: %v", err)
-				s.renderError(w, http.StatusInternalServerError, "Connector Login Error")
+				s.renderError(r, w, http.StatusInternalServerError, "Connector Login Error")
 				return
 			}
 
@@ -358,12 +358,12 @@ func (s *Server) handleConnectorLogin(w http.ResponseWriter, r *http.Request) {
 			  </body>
 			  </html>`, action, value, authReqID)
 		default:
-			s.renderError(w, http.StatusBadRequest, "Requested resource does not exist.")
+			s.renderError(r, w, http.StatusBadRequest, "Requested resource does not exist.")
 		}
 	case http.MethodPost:
 		passwordConnector, ok := conn.Connector.(connector.PasswordConnector)
 		if !ok {
-			s.renderError(w, http.StatusBadRequest, "Requested resource does not exist.")
+			s.renderError(r, w, http.StatusBadRequest, "Requested resource does not exist.")
 			return
 		}
 
@@ -373,11 +373,11 @@ func (s *Server) handleConnectorLogin(w http.ResponseWriter, r *http.Request) {
 		identity, ok, err := passwordConnector.Login(r.Context(), scopes, username, password)
 		if err != nil {
 			s.logger.Errorf("Failed to login user: %v", err)
-			s.renderError(w, http.StatusInternalServerError, fmt.Sprintf("Login error: %v", err))
+			s.renderError(r, w, http.StatusInternalServerError, fmt.Sprintf("Login error: %v", err))
 			return
 		}
 		if !ok {
-			if err := s.templates.password(w, r.URL.String(), username, usernamePrompt(passwordConnector), true, showBacklink); err != nil {
+			if err := s.templates.password(r, w, r.URL.String(), username, usernamePrompt(passwordConnector), true, showBacklink, r.URL.Path); err != nil {
 				s.logger.Errorf("Server template error: %v", err)
 			}
 			return
@@ -385,13 +385,13 @@ func (s *Server) handleConnectorLogin(w http.ResponseWriter, r *http.Request) {
 		redirectURL, err := s.finalizeLogin(identity, authReq, conn.Connector)
 		if err != nil {
 			s.logger.Errorf("Failed to finalize login: %v", err)
-			s.renderError(w, http.StatusInternalServerError, "Login error.")
+			s.renderError(r, w, http.StatusInternalServerError, "Login error.")
 			return
 		}
 
 		http.Redirect(w, r, redirectURL, http.StatusSeeOther)
 	default:
-		s.renderError(w, http.StatusBadRequest, "Unsupported request method.")
+		s.renderError(r, w, http.StatusBadRequest, "Unsupported request method.")
 	}
 }
 
@@ -400,16 +400,16 @@ func (s *Server) handleConnectorCallback(w http.ResponseWriter, r *http.Request)
 	switch r.Method {
 	case http.MethodGet: // OAuth2 callback
 		if authID = r.URL.Query().Get("state"); authID == "" {
-			s.renderError(w, http.StatusBadRequest, "User session error.")
+			s.renderError(r, w, http.StatusBadRequest, "User session error.")
 			return
 		}
 	case http.MethodPost: // SAML POST binding
 		if authID = r.PostFormValue("RelayState"); authID == "" {
-			s.renderError(w, http.StatusBadRequest, "User session error.")
+			s.renderError(r, w, http.StatusBadRequest, "User session error.")
 			return
 		}
 	default:
-		s.renderError(w, http.StatusBadRequest, "Method not supported")
+		s.renderError(r, w, http.StatusBadRequest, "Method not supported")
 		return
 	}
 
@@ -417,24 +417,24 @@ func (s *Server) handleConnectorCallback(w http.ResponseWriter, r *http.Request)
 	if err != nil {
 		if err == storage.ErrNotFound {
 			s.logger.Errorf("Invalid 'state' parameter provided: %v", err)
-			s.renderError(w, http.StatusBadRequest, "Requested resource does not exist.")
+			s.renderError(r, w, http.StatusBadRequest, "Requested resource does not exist.")
 			return
 		}
 		s.logger.Errorf("Failed to get auth request: %v", err)
-		s.renderError(w, http.StatusInternalServerError, "Database error.")
+		s.renderError(r, w, http.StatusInternalServerError, "Database error.")
 		return
 	}
 
 	if connID := mux.Vars(r)["connector"]; connID != "" && connID != authReq.ConnectorID {
 		s.logger.Errorf("Connector mismatch: authentication started with id %q, but callback for id %q was triggered", authReq.ConnectorID, connID)
-		s.renderError(w, http.StatusInternalServerError, "Requested resource does not exist.")
+		s.renderError(r, w, http.StatusInternalServerError, "Requested resource does not exist.")
 		return
 	}
 
 	conn, err := s.getConnector(authReq.ConnectorID)
 	if err != nil {
 		s.logger.Errorf("Failed to get connector with id %q : %v", authReq.ConnectorID, err)
-		s.renderError(w, http.StatusInternalServerError, "Requested resource does not exist.")
+		s.renderError(r, w, http.StatusInternalServerError, "Requested resource does not exist.")
 		return
 	}
 
@@ -443,32 +443,32 @@ func (s *Server) handleConnectorCallback(w http.ResponseWriter, r *http.Request)
 	case connector.CallbackConnector:
 		if r.Method != http.MethodGet {
 			s.logger.Errorf("SAML request mapped to OAuth2 connector")
-			s.renderError(w, http.StatusBadRequest, "Invalid request")
+			s.renderError(r, w, http.StatusBadRequest, "Invalid request")
 			return
 		}
 		identity, err = conn.HandleCallback(parseScopes(authReq.Scopes), r)
 	case connector.SAMLConnector:
 		if r.Method != http.MethodPost {
 			s.logger.Errorf("OAuth2 request mapped to SAML connector")
-			s.renderError(w, http.StatusBadRequest, "Invalid request")
+			s.renderError(r, w, http.StatusBadRequest, "Invalid request")
 			return
 		}
 		identity, err = conn.HandlePOST(parseScopes(authReq.Scopes), r.PostFormValue("SAMLResponse"), authReq.ID)
 	default:
-		s.renderError(w, http.StatusInternalServerError, "Requested resource does not exist.")
+		s.renderError(r, w, http.StatusInternalServerError, "Requested resource does not exist.")
 		return
 	}
 
 	if err != nil {
 		s.logger.Errorf("Failed to authenticate: %v", err)
-		s.renderError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to authenticate: %v", err))
+		s.renderError(r, w, http.StatusInternalServerError, fmt.Sprintf("Failed to authenticate: %v", err))
 		return
 	}
 
 	redirectURL, err := s.finalizeLogin(identity, authReq, conn.Connector)
 	if err != nil {
 		s.logger.Errorf("Failed to finalize login: %v", err)
-		s.renderError(w, http.StatusInternalServerError, "Login error.")
+		s.renderError(r, w, http.StatusInternalServerError, "Login error.")
 		return
 	}
 
@@ -511,12 +511,12 @@ func (s *Server) handleApproval(w http.ResponseWriter, r *http.Request) {
 	authReq, err := s.storage.GetAuthRequest(r.FormValue("req"))
 	if err != nil {
 		s.logger.Errorf("Failed to get auth request: %v", err)
-		s.renderError(w, http.StatusInternalServerError, "Database error.")
+		s.renderError(r, w, http.StatusInternalServerError, "Database error.")
 		return
 	}
 	if !authReq.LoggedIn {
 		s.logger.Errorf("Auth request does not have an identity for approval")
-		s.renderError(w, http.StatusInternalServerError, "Login process not yet finalized.")
+		s.renderError(r, w, http.StatusInternalServerError, "Login process not yet finalized.")
 		return
 	}
 
@@ -529,15 +529,15 @@ func (s *Server) handleApproval(w http.ResponseWriter, r *http.Request) {
 		client, err := s.storage.GetClient(authReq.ClientID)
 		if err != nil {
 			s.logger.Errorf("Failed to get client %q: %v", authReq.ClientID, err)
-			s.renderError(w, http.StatusInternalServerError, "Failed to retrieve client.")
+			s.renderError(r, w, http.StatusInternalServerError, "Failed to retrieve client.")
 			return
 		}
-		if err := s.templates.approval(w, authReq.ID, authReq.Claims.Username, client.Name, authReq.Scopes); err != nil {
+		if err := s.templates.approval(r, w, authReq.ID, authReq.Claims.Username, client.Name, authReq.Scopes, r.URL.Path); err != nil {
 			s.logger.Errorf("Server template error: %v", err)
 		}
 	case http.MethodPost:
 		if r.FormValue("approval") != "approve" {
-			s.renderError(w, http.StatusInternalServerError, "Approval rejected.")
+			s.renderError(r, w, http.StatusInternalServerError, "Approval rejected.")
 			return
 		}
 		s.sendCodeResponse(w, r, authReq)
@@ -546,22 +546,22 @@ func (s *Server) handleApproval(w http.ResponseWriter, r *http.Request) {
 
 func (s *Server) sendCodeResponse(w http.ResponseWriter, r *http.Request, authReq storage.AuthRequest) {
 	if s.now().After(authReq.Expiry) {
-		s.renderError(w, http.StatusBadRequest, "User session has expired.")
+		s.renderError(r, w, http.StatusBadRequest, "User session has expired.")
 		return
 	}
 
 	if err := s.storage.DeleteAuthRequest(authReq.ID); err != nil {
 		if err != storage.ErrNotFound {
 			s.logger.Errorf("Failed to delete authorization request: %v", err)
-			s.renderError(w, http.StatusInternalServerError, "Internal server error.")
+			s.renderError(r, w, http.StatusInternalServerError, "Internal server error.")
 		} else {
-			s.renderError(w, http.StatusBadRequest, "User session error.")
+			s.renderError(r, w, http.StatusBadRequest, "User session error.")
 		}
 		return
 	}
 	u, err := url.Parse(authReq.RedirectURI)
 	if err != nil {
-		s.renderError(w, http.StatusInternalServerError, "Invalid redirect URI.")
+		s.renderError(r, w, http.StatusInternalServerError, "Invalid redirect URI.")
 		return
 	}
 
@@ -598,14 +598,14 @@ func (s *Server) sendCodeResponse(w http.ResponseWriter, r *http.Request, authRe
 			}
 			if err := s.storage.CreateAuthCode(code); err != nil {
 				s.logger.Errorf("Failed to create auth code: %v", err)
-				s.renderError(w, http.StatusInternalServerError, "Internal server error.")
+				s.renderError(r, w, http.StatusInternalServerError, "Internal server error.")
 				return
 			}
 
 			// Implicit and hybrid flows that try to use the OOB redirect URI are
 			// rejected earlier. If we got here we're using the code flow.
 			if authReq.RedirectURI == redirectURIOOB {
-				if err := s.templates.oob(w, code.ID); err != nil {
+				if err := s.templates.oob(r, w, code.ID, r.URL.Path); err != nil {
 					s.logger.Errorf("Server template error: %v", err)
 				}
 				return
@@ -1119,8 +1119,8 @@ func (s *Server) writeAccessToken(w http.ResponseWriter, idToken, accessToken, r
 	w.Write(data)
 }
 
-func (s *Server) renderError(w http.ResponseWriter, status int, description string) {
-	if err := s.templates.err(w, status, description); err != nil {
+func (s *Server) renderError(r *http.Request, w http.ResponseWriter, status int, description string) {
+	if err := s.templates.err(r, w, status, description); err != nil {
 		s.logger.Errorf("Server template error: %v", err)
 	}
 }
diff --git a/server/templates.go b/server/templates.go
index 89d413719d855221da521d28da8b647c591ee8ec..88aeace0bd2994641f11fec005073bfe05150d6f 100644
--- a/server/templates.go
+++ b/server/templates.go
@@ -6,7 +6,9 @@ import (
 	"io"
 	"io/ioutil"
 	"net/http"
+	"net/url"
 	"os"
+	"path"
 	"path/filepath"
 	"sort"
 	"strings"
@@ -94,7 +96,7 @@ func loadWebConfig(c webConfig) (static, theme http.Handler, templates *template
 		c.dir = "./web"
 	}
 	if c.logoURL == "" {
-		c.logoURL = join(c.issuerURL, "theme/logo.png")
+		c.logoURL = "theme/logo.png"
 	}
 
 	if err := dirExists(c.dir); err != nil {
@@ -136,10 +138,15 @@ func loadTemplates(c webConfig, templatesDir string) (*templates, error) {
 		return nil, fmt.Errorf("no files in template dir %q", templatesDir)
 	}
 
+	issuerURL, err := url.Parse(c.issuerURL)
+	if err != nil {
+		return nil, fmt.Errorf("error parsing issuerURL: %v", err)
+	}
+
 	funcs := map[string]interface{}{
 		"issuer": func() string { return c.issuer },
 		"logo":   func() string { return c.logoURL },
-		"url":    func(s string) string { return join(c.issuerURL, s) },
+		"url":    func(reqPath, assetPath string) string { return relativeURL(issuerURL.Path, reqPath, assetPath) },
 		"lower":  strings.ToLower,
 		"extra":  func(k string) string { return c.extra[k] },
 	}
@@ -166,6 +173,69 @@ func loadTemplates(c webConfig, templatesDir string) (*templates, error) {
 	}, nil
 }
 
+// relativeURL returns the URL of the asset relative to the URL of the request path.
+// The serverPath is consulted to trim any prefix due in case it is not listening
+// to the root path.
+//
+// Algorithm:
+// 1. Remove common prefix of serverPath and reqPath
+// 2. Remove common prefix of assetPath and reqPath
+// 3. For each part of reqPath remaining(minus one), go up one level (..)
+// 4. For each part of assetPath remaining, append it to result
+//
+//eg
+//server listens at localhost/dex so serverPath is dex
+//reqPath is /dex/auth
+//assetPath is static/main.css
+//relativeURL("/dex", "/dex/auth", "static/main.css") = "../static/main.css"
+func relativeURL(serverPath, reqPath, assetPath string) string {
+
+	splitPath := func(p string) []string {
+		res := []string{}
+		parts := strings.Split(path.Clean(p), "/")
+		for _, part := range parts {
+			if part != "" {
+				res = append(res, part)
+			}
+		}
+		return res
+	}
+
+	stripCommonParts := func(s1, s2 []string) ([]string, []string) {
+		min := len(s1)
+		if len(s2) < min {
+			min = len(s2)
+		}
+
+		splitIndex := min
+		for i := 0; i < min; i++ {
+			if s1[i] != s2[i] {
+				splitIndex = i
+				break
+			}
+		}
+		return s1[splitIndex:], s2[splitIndex:]
+	}
+
+	server, req, asset := splitPath(serverPath), splitPath(reqPath), splitPath(assetPath)
+
+	// Remove common prefix of request path with server path
+	server, req = stripCommonParts(server, req)
+
+	// Remove common prefix of request path with asset path
+	asset, req = stripCommonParts(asset, req)
+
+	// For each part of the request remaining (minus one) -> go up one level (..)
+	// For each part of the asset remaining               -> append it
+	var relativeURL string
+	for i := 0; i < len(req)-1; i++ {
+		relativeURL = path.Join("..", relativeURL)
+	}
+	relativeURL = path.Join(relativeURL, path.Join(asset...))
+
+	return relativeURL
+}
+
 var scopeDescriptions = map[string]string{
 	"offline_access": "Have offline access",
 	"profile":        "View basic profile information",
@@ -184,26 +254,28 @@ func (n byName) Len() int           { return len(n) }
 func (n byName) Less(i, j int) bool { return n[i].Name < n[j].Name }
 func (n byName) Swap(i, j int)      { n[i], n[j] = n[j], n[i] }
 
-func (t *templates) login(w http.ResponseWriter, connectors []connectorInfo) error {
+func (t *templates) login(r *http.Request, w http.ResponseWriter, connectors []connectorInfo, reqPath string) error {
 	sort.Sort(byName(connectors))
 	data := struct {
 		Connectors []connectorInfo
-	}{connectors}
+		ReqPath    string
+	}{connectors, r.URL.Path}
 	return renderTemplate(w, t.loginTmpl, data)
 }
 
-func (t *templates) password(w http.ResponseWriter, postURL, lastUsername, usernamePrompt string, lastWasInvalid, showBacklink bool) error {
+func (t *templates) password(r *http.Request, w http.ResponseWriter, postURL, lastUsername, usernamePrompt string, lastWasInvalid, showBacklink bool, reqPath string) error {
 	data := struct {
 		PostURL        string
 		BackLink       bool
 		Username       string
 		UsernamePrompt string
 		Invalid        bool
-	}{postURL, showBacklink, lastUsername, usernamePrompt, lastWasInvalid}
+		ReqPath        string
+	}{postURL, showBacklink, lastUsername, usernamePrompt, lastWasInvalid, r.URL.Path}
 	return renderTemplate(w, t.passwordTmpl, data)
 }
 
-func (t *templates) approval(w http.ResponseWriter, authReqID, username, clientName string, scopes []string) error {
+func (t *templates) approval(r *http.Request, w http.ResponseWriter, authReqID, username, clientName string, scopes []string, reqPath string) error {
 	accesses := []string{}
 	for _, scope := range scopes {
 		access, ok := scopeDescriptions[scope]
@@ -217,23 +289,26 @@ func (t *templates) approval(w http.ResponseWriter, authReqID, username, clientN
 		Client    string
 		AuthReqID string
 		Scopes    []string
-	}{username, clientName, authReqID, accesses}
+		ReqPath   string
+	}{username, clientName, authReqID, accesses, r.URL.Path}
 	return renderTemplate(w, t.approvalTmpl, data)
 }
 
-func (t *templates) oob(w http.ResponseWriter, code string) error {
+func (t *templates) oob(r *http.Request, w http.ResponseWriter, code string, reqPath string) error {
 	data := struct {
-		Code string
-	}{code}
+		Code    string
+		ReqPath string
+	}{code, r.URL.Path}
 	return renderTemplate(w, t.oobTmpl, data)
 }
 
-func (t *templates) err(w http.ResponseWriter, errCode int, errMsg string) error {
+func (t *templates) err(r *http.Request, w http.ResponseWriter, errCode int, errMsg string) error {
 	w.WriteHeader(errCode)
 	data := struct {
 		ErrType string
 		ErrMsg  string
-	}{http.StatusText(errCode), errMsg}
+		ReqPath string
+	}{http.StatusText(errCode), errMsg, r.URL.Path}
 	if err := t.errorTmpl.Execute(w, data); err != nil {
 		return fmt.Errorf("Error rendering template %s: %s", t.errorTmpl.Name(), err)
 	}
diff --git a/server/templates_test.go b/server/templates_test.go
index abb4e431abd516750a5a1e5e2b77073c236b8f9e..5ead66e591b770c41e5adfb4722596c46424d122 100644
--- a/server/templates_test.go
+++ b/server/templates_test.go
@@ -1 +1,44 @@
 package server
+
+import "testing"
+
+func TestRelativeURL(t *testing.T) {
+	tests := []struct {
+		name       string
+		serverPath string
+		reqPath    string
+		assetPath  string
+		expected   string
+	}{
+		{
+			name:       "server-root-req-one-level-asset-two-level",
+			serverPath: "/",
+			reqPath:    "/auth",
+			assetPath:  "/theme/main.css",
+			expected:   "theme/main.css",
+		},
+		{
+			name:       "server-one-level-req-one-level-asset-two-level",
+			serverPath: "/dex",
+			reqPath:    "/dex/auth",
+			assetPath:  "/theme/main.css",
+			expected:   "theme/main.css",
+		},
+		{
+			name:       "server-root-req-two-level-asset-three-level",
+			serverPath: "/dex",
+			reqPath:    "/dex/auth/connector",
+			assetPath:  "assets/css/main.css",
+			expected:   "../assets/css/main.css",
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			actual := relativeURL(test.serverPath, test.reqPath, test.assetPath)
+			if actual != test.expected {
+				t.Fatalf("Got '%s'. Expected '%s'", actual, test.expected)
+			}
+		})
+	}
+}
diff --git a/web/templates/header.html b/web/templates/header.html
index edd6289a2b501b083b725177cdb8f5fdcc149070..0d4fea0fa23d2b2485a6e5fbdb8503dba5d71846 100644
--- a/web/templates/header.html
+++ b/web/templates/header.html
@@ -5,15 +5,15 @@
     <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
     <title>{{ issuer }}</title>
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <link href="{{ url "static/main.css" }}" rel="stylesheet">
-    <link href="{{ url "theme/styles.css" }}" rel="stylesheet">
-    <link rel="icon" href="{{ url "theme/favicon.png" }}">
+    <link href="{{ url .ReqPath "static/main.css" }}" rel="stylesheet">
+    <link href="{{ url .ReqPath "theme/styles.css" }}" rel="stylesheet">
+    <link rel="icon" href="{{ url .ReqPath "theme/favicon.png" }}">
   </head>
 
   <body class="theme-body">
     <div class="theme-navbar">
       <div class="theme-navbar__logo-wrap">
-        <img class="theme-navbar__logo" src="{{ logo }}">
+        <img class="theme-navbar__logo" src="{{ url .ReqPath logo }}">
       </div>
     </div>