diff --git a/config.go b/config.go
index d49d4bdb957ade9609cf087a90d1340381cbddc1..5c4509f811e5350bf7b00a823a28c0f9c338bc9a 100644
--- a/config.go
+++ b/config.go
@@ -173,7 +173,7 @@ func (r *Config) isValid() error {
 					return errors.New("the security filter must be switch on for this feature: hostnames")
 				}
 			}
-			if r.EnableEncryptedToken && r.EncryptionKey == "" {
+			if (r.EnableEncryptedToken || r.ForceEncryptedCookie) && r.EncryptionKey == "" {
 				return errors.New("you have not specified an encryption key for encoding the access token")
 			}
 			if r.EnableRefreshTokens && r.EncryptionKey == "" {
diff --git a/doc.go b/doc.go
index cfa461d43099ed7bcac2a75bc938c2f406cd5a6b..f0686fdf266702de2e92b20e68e604a624779f18 100644
--- a/doc.go
+++ b/doc.go
@@ -211,6 +211,8 @@ type Config struct {
 	EnableDefaultDeny bool `json:"enable-default-deny" yaml:"enable-default-deny" usage:"enables a default denial on all requests, you have to explicitly say what is permitted (recommended)"`
 	// EnableEncryptedToken indicates the access token should be encoded
 	EnableEncryptedToken bool `json:"enable-encrypted-token" yaml:"enable-encrypted-token" usage:"enable encryption for the access tokens"`
+	// ForceEncryptedCookie indicates that the access token in the cookie should be encoded, regardless what EnableEncryptedToken says. This way, gatekeeper may receive tokens in header in the clear, whereas tokens in cookies remain encrypted
+	ForceEncryptedCookie bool `json:"force-encrypted-cookie" yaml:"force-encrypted-cookie" usage:"force encryption for the access tokens in cookies"`
 	// EnableLogging indicates if we should log all the requests
 	EnableLogging bool `json:"enable-logging" yaml:"enable-logging" usage:"enable http logging of the requests"`
 	// EnableJSONLogging is the logging format
@@ -227,7 +229,7 @@ type Config struct {
 	EnableLoginHandler bool `json:"enable-login-handler" yaml:"enable-login-handler" usage:"enables the handling of the refresh tokens" env:"ENABLE_LOGIN_HANDLER"`
 	// EnableTokenHeader adds the JWT token to the upstream authentication headers
 	EnableTokenHeader bool `json:"enable-token-header" yaml:"enable-token-header" usage:"enables the token authentication header X-Auth-Token to upstream"`
-	// EnableAuthorizationHeader indicates we should pass the authorization header
+	// EnableAuthorizationHeader indicates we should pass the authorization header to the upstream endpoint
 	EnableAuthorizationHeader bool `json:"enable-authorization-header" yaml:"enable-authorization-header" usage:"adds the authorization header to the proxy request" env:"ENABLE_AUTHORIZATION_HEADER"`
 	// EnableAuthorizationCookies indicates we should pass the authorization cookies to the upstream endpoint
 	EnableAuthorizationCookies bool `json:"enable-authorization-cookies" yaml:"enable-authorization-cookies" usage:"adds the authorization cookies to the uptream proxy request" env:"ENABLE_AUTHORIZATION_COOKIES"`
diff --git a/handlers.go b/handlers.go
index bc7be1b695d9490abb6d69b1a7ad008ea1eb9f00..64a5c55754793f69eb74d6ef9b8f8a422b09f909 100644
--- a/handlers.go
+++ b/handlers.go
@@ -142,8 +142,8 @@ func (r *oauthProxy) oauthCallbackHandler(w http.ResponseWriter, req *http.Reque
 		return
 	}
 
-	// Flow: once we exchange the authorization code we parse the ID Token; we then check for a access token,
-	// if a access token is present and we can decode it, we use that as the session token, otherwise we default
+	// Flow: once we exchange the authorization code we parse the ID Token; we then check for an access token,
+	// if an access token is present and we can decode it, we use that as the session token, otherwise we default
 	// to the ID Token.
 	token, identity, err := parseToken(resp.IDToken)
 	if err != nil {
@@ -168,7 +168,7 @@ func (r *oauthProxy) oauthCallbackHandler(w http.ResponseWriter, req *http.Reque
 	accessToken := token.Encode()
 
 	// step: are we encrypting the access token?
-	if r.config.EnableEncryptedToken {
+	if r.config.EnableEncryptedToken || r.config.ForceEncryptedCookie {
 		if accessToken, err = encodeText(accessToken, r.config.EncryptionKey); err != nil {
 			r.log.Error("unable to encode the access token", zap.Error(err))
 			w.WriteHeader(http.StatusInternalServerError)
@@ -181,10 +181,10 @@ func (r *oauthProxy) oauthCallbackHandler(w http.ResponseWriter, req *http.Reque
 		zap.String("expires", identity.ExpiresAt.Format(time.RFC3339)),
 		zap.String("duration", time.Until(identity.ExpiresAt).String()))
 
-	// @metric a token has beeb issued
+	// @metric a token has been issued
 	oauthTokensMetric.WithLabelValues("issued").Inc()
 
-	// step: does the response has a refresh token and we are NOT ignore refresh tokens?
+	// step: does the response have a refresh token and we do NOT ignore refresh tokens?
 	if r.config.EnableRefreshTokens && resp.RefreshToken != "" {
 		var encrypted string
 		encrypted, err = encodeText(resp.RefreshToken, r.config.EncryptionKey)
diff --git a/middleware.go b/middleware.go
index 38c483531b3e57c65f690f60bb1d0821e513c493..f1ea568aaf192ecfe8184bd59a6128c91bc9334d 100644
--- a/middleware.go
+++ b/middleware.go
@@ -194,7 +194,7 @@ func (r *oauthProxy) authenticationMiddleware(resource *Resource) func(http.Hand
 						zap.Duration("expires_in", time.Until(exp)))
 
 					accessToken := token.Encode()
-					if r.config.EnableEncryptedToken {
+					if r.config.EnableEncryptedToken || r.config.ForceEncryptedCookie {
 						if accessToken, err = encodeText(accessToken, r.config.EncryptionKey); err != nil {
 							r.log.Error("unable to encode the access token", zap.Error(err))
 							w.WriteHeader(http.StatusInternalServerError)
diff --git a/middleware_test.go b/middleware_test.go
index 09fa77a9c1a053314ef1412a3affdb6b13f8c289..94e8f9a53f35a51dc47d03c56124a17484a3e16b 100644
--- a/middleware_test.go
+++ b/middleware_test.go
@@ -34,6 +34,10 @@ import (
 	"gopkg.in/resty.v1"
 )
 
+const (
+	testEncryptionKey = "ZSeCYDUxIlhDrmPpa1Ldc7il384esSF2"
+)
+
 type fakeRequest struct {
 	BasicAuth               bool
 	Cookies                 []*http.Cookie
@@ -66,6 +70,9 @@ type fakeRequest struct {
 	ExpectedNoProxyHeaders  []string
 	ExpectedProxy           bool
 	ExpectedProxyHeaders    map[string]string
+
+	// advanced test cases
+	ExpectedCookiesValidator map[string]func(string) bool
 }
 
 type fakeProxy struct {
@@ -272,6 +279,15 @@ func (f *fakeProxy) RunTests(t *testing.T, requests []fakeRequest) {
 					assert.Equal(t, cookie.Value, v, "case %d, expected cookie value: %s, got: %s", i, v, cookie.Value)
 				}
 			}
+			for k, v := range c.ExpectedCookiesValidator {
+				cookie := findCookie(k, resp.Cookies())
+				if !assert.NotNil(t, cookie, "case %d, expected cookie %s not found", i, k) {
+					continue
+				}
+				if v != nil {
+					assert.True(t, v(cookie.Value), "case %d, invalid cookie value: %s", i, cookie.Value)
+				}
+			}
 		}
 		if c.OnResponse != nil {
 			c.OnResponse(i, request, resp)
@@ -1072,7 +1088,7 @@ func TestCrossSiteHandler(t *testing.T) {
 func TestCheckRefreshTokens(t *testing.T) {
 	cfg := newFakeKeycloakConfig()
 	cfg.EnableRefreshTokens = true
-	cfg.EncryptionKey = "ZSeCYDUxIlhDrmPpa1Ldc7il384esSF2"
+	cfg.EncryptionKey = testEncryptionKey
 	fn := func(no int, req *resty.Request, resp *resty.Response) {
 		if no == 0 {
 			<-time.After(1000 * time.Millisecond)
@@ -1101,6 +1117,73 @@ func TestCheckRefreshTokens(t *testing.T) {
 	p.RunTests(t, requests)
 }
 
+func TestCheckEncryptedCookie(t *testing.T) {
+	cfg := newFakeKeycloakConfig()
+	cfg.EnableRefreshTokens = true
+	cfg.EnableEncryptedToken = true
+	cfg.Verbose = true
+	cfg.EnableLogging = true
+	cfg.EncryptionKey = testEncryptionKey
+	testEncryptedToken(t, cfg)
+}
+
+func TestCheckForcedEncryptedCookie(t *testing.T) {
+	cfg := newFakeKeycloakConfig()
+	cfg.EnableRefreshTokens = true
+	cfg.EnableEncryptedToken = false
+	cfg.ForceEncryptedCookie = true
+	cfg.Verbose = true
+	cfg.EnableLogging = true
+	cfg.EncryptionKey = testEncryptionKey
+	testEncryptedToken(t, cfg)
+}
+
+func testEncryptedToken(t *testing.T, cfg *Config) {
+	fn := func(no int, req *resty.Request, resp *resty.Response) {
+		if no == 0 {
+			<-time.After(1000 * time.Millisecond)
+		}
+	}
+	val := func(value string) bool {
+		// check the cookie value is an encrypted token
+		accessToken, err := decodeText(value, cfg.EncryptionKey)
+		if err != nil {
+			return false
+		}
+		jwt, err := jose.ParseJWT(accessToken)
+		if err != nil {
+			return false
+		}
+		claims, err := jwt.Claims()
+		if err != nil {
+			return false
+		}
+		return assert.Contains(t, claims, "aud") && assert.Contains(t, claims, "email")
+	}
+	p := newFakeProxy(cfg)
+	p.idp.setTokenExpiration(1000 * time.Millisecond)
+
+	requests := []fakeRequest{
+		{
+			URI:           fakeAuthAllURL,
+			HasLogin:      true,
+			Redirects:     true,
+			OnResponse:    fn,
+			ExpectedProxy: true,
+			ExpectedCode:  http.StatusOK,
+		},
+		{
+			URI:                      fakeAuthAllURL,
+			Redirects:                false,
+			ExpectedProxy:            true,
+			ExpectedCode:             http.StatusOK,
+			ExpectedCookies:          map[string]string{cfg.CookieAccessName: ""},
+			ExpectedCookiesValidator: map[string]func(string) bool{cfg.CookieAccessName: val},
+		},
+	}
+	p.RunTests(t, requests)
+}
+
 func TestCustomHeadersHandler(t *testing.T) {
 	requests := []struct {
 		Match   []string
diff --git a/session.go b/session.go
index eff7363a5ef1b365c4d0e7ee54dd9e69c2f114e4..76a307070948e710fbb81d04a376d30b144fb661 100644
--- a/session.go
+++ b/session.go
@@ -33,7 +33,7 @@ func (r *oauthProxy) getIdentity(req *http.Request) (*userContext, error) {
 	if err != nil {
 		return nil, err
 	}
-	if r.config.EnableEncryptedToken {
+	if r.config.EnableEncryptedToken || r.config.ForceEncryptedCookie && !isBearer {
 		if access, err = decodeText(access, r.config.EncryptionKey); err != nil {
 			return nil, ErrDecryption
 		}