diff --git a/README.md b/README.md
index 7e93baffd3ac15217dc9e4e049c80a9b3c37858c..74e25bdf1f0823419d4d7a956e8a05a26b91f11f 100644
--- a/README.md
+++ b/README.md
@@ -27,7 +27,7 @@
 Keycloak-proxy is a proxy service which at the risk of stating the obvious integrates with the [Keycloak](https://github.com/keycloak/keycloak) authentication service. Although technically the service has no dependency on Keycloak itself and would quite happily work with any OpenID provider. The service supports both access tokens in browser cookie or bearer tokens.
 
 ```shell
-$ bin/keycloak-proxy help
+$ bin/keycloak-proxy --help
 NAME:
    keycloak-proxy - is a proxy using the keycloak service for auth and authorization
 
@@ -35,7 +35,7 @@ USAGE:
    keycloak-proxy [options]
 
 VERSION:
-   v2.2.0 (git+sha: 72a3646-dirty, built: 25-05-2018)
+   v2.2.2 (git+sha: c4d677a-dirty, built: 12-07-2018)
 
 AUTHOR:
    Rohith <gambol99@gmail.com>
@@ -55,6 +55,7 @@ GLOBAL OPTIONS:
    --skip-openid-provider-tls-verify         skip the verification of any TLS communication with the openid provider (default: false)
    --openid-provider-proxy value             proxy for communication with the openid provider
    --openid-provider-timeout value           timeout for openid configuration on .well-known/openid-configuration (default: 30s)
+   --base-uri value                          common prefix for all URIs [$PROXY_BASE_URI]
    --oauth-uri value                         the uri for proxy oauth endpoints (default: "/oauth") [$PROXY_OAUTH_URI]
    --scopes value                            list of scopes requested when authenticating the user
    --upstream-url value                      url for the upstream endpoint you wish to proxy [$PROXY_UPSTREAM_URL]
@@ -62,15 +63,21 @@ GLOBAL OPTIONS:
    --resources value                         list of resources 'uri=/admin*|methods=GET,PUT|roles=role1,role2'
    --headers value                           custom headers to the upstream request, key=value
    --preserve-host                           preserve the host header of the proxied request in the upstream request (default: false)
+   --request-id-header value                 the http header name for request id (default: "X-Request-ID") [$PROXY_REQUEST_ID_HEADER]
+   --response-headers value                  custom headers to added to the http response key=value
+   --enable-self-signed-tls                  create self signed certificates for the proxy (default: false) [$PROXY_ENABLE_SELF_SIGNED_TLS]
+   --self-signed-tls-hostnames value         a list of hostnames to place on the self-signed certificate
+   --self-signed-tls-expiration value        the expiration of the certificate before rotation (default: 3h0m0s)
+   --enable-request-id                       indicates we should add a request id if none found (default: false) [$PROXY_ENABLE_REQUEST_ID]
    --enable-logout-redirect                  indicates we should redirect to the identity provider for logging out (default: false)
-   --enable-default-deny                     enables a default denial on all requests, you have to explicitly say what is permitted (recommended) (default: false)
+   --enable-default-deny                     enables a default denial on all requests, you have to explicitly say what is permitted (recommended) (default: true)
    --enable-encrypted-token                  enable encryption for the access tokens (default: false)
    --enable-logging                          enable http logging of the requests (default: false)
    --enable-json-logging                     switch on json logging rather than text (default: false)
    --enable-forwarding                       enables the forwarding proxy mode, signing outbound request (default: false)
    --enable-security-filter                  enables the security filter handler (default: false) [$PROXY_ENABLE_SECURITY_FILTER]
    --enable-refresh-tokens                   enables the handling of the refresh tokens (default: false) [$PROXY_ENABLE_REFRESH_TOKEN]
-   --enable-session-cookies                  access and refresh tokens are session only i.e. removed browser close (default: false)
+   --enable-session-cookies                  access and refresh tokens are session only i.e. removed browser close (default: true)
    --enable-login-handler                    enables the handling of the refresh tokens (default: false) [$PROXY_ENABLE_LOGIN_HANDLER]
    --enable-token-header                     enables the token authentication header X-Auth-Token to upstream (default: true)
    --enable-authorization-header             adds the authorization header to the proxy request (default: true) [$PROXY_ENABLE_AUTHORIZATION_HEADER]
@@ -107,6 +114,7 @@ GLOBAL OPTIONS:
    --hostnames value                         list of hostnames the service will respond to
    --store-url value                         url for the storage subsystem, e.g redis://127.0.0.1:6379, file:///etc/tokens.file
    --encryption-key value                    encryption key used to encryption the session state [$PROXY_ENCRYPTION_KEY]
+   --invalid-auth-redirects-with-303         use HTTP 303 redirects instead of 307 for invalid auth tokens (default: false)
    --no-redirects                            do not have back redirects when no authentication is present, 401 them (default: false)
    --skip-token-verification                 TESTING ONLY; bypass token verification, only expiration and roles enforced (default: false)
    --upstream-keepalives                     enables or disables the keepalive connections for upstream endpoint (default: true)
@@ -138,8 +146,8 @@ GLOBAL OPTIONS:
 Assuming you have make + go, simply run make (or 'make static' for static linking). You can also build via docker container: make docker-build
 
 #### **Docker image**
-Docker image is available at [https://quay.io/repository/gambol99/keycloak-proxy](https://quay.io/repository/gambol99/keycloak-proxy)
 
+Docker image is available at [https://quay.io/repository/gambol99/keycloak-proxy](https://quay.io/repository/gambol99/keycloak-proxy)
 
 #### **Configuration**
 
@@ -269,6 +277,23 @@ By default all requests will be proxyed on to the upstream, if you wish to ensur
 
 Note the HTTP routing rules following the guidelines from [chi](https://github.com/go-chi/chi#router-design). Its also worth nothing the ordering of the resource do not matter, the router will handle that for you.
 
+#### **Resources**
+
+The resources defined either on the command line as `--resources` or via a configuration file defines a collection of enrtypoints and the requirement for access.
+
+```YAML
+resources:
+- uri: /admin/*
+  roles:
+  - admin
+  - superuser
+  # will work with either 'admin' or 'superuser' the default is false and requires both roles present
+  require-any-role: true
+- uri: /public/*
+  # indicates we permit access regardless
+  white-listed: true
+```
+
 #### **Google OAuth**
 
 Although the role extensions do require a Keycloak IDP or at the very least a IDP that produces a token which contains roles, there's nothing stopping you from using it against any OpenID providers, such as Google. Go to the Google Developers Console/Google Cloud Console and create a new OAuth 2.0 client ID *(via "API Manager-> Credentials)*. Once you've created the OAuth 2.0 client ID, take the client ID, secret and make sure you've added the callback url to the application scope *(using the default this would be http://127.0.0.1:3000/oauth/callback)*
@@ -430,6 +455,10 @@ X-Auth-Given-Name: Rohith
 X-Auth-Name: Rohith Jayawardene
 ```
 
+#### **Self Signed Certificate**
+
+The proxy can be instructed to generate it's on self-signed certificate which are rotated on a user-defined expiration. Add the `--enable-self-signed-tls=true` option to the config or command line and if required you can configure the hostnames and expiration via the `--self-signed-tls-hostnames` and `--self-signed-tls-expiration`
+
 #### **Encryption Key**
 
 In order to remain stateless and not have to rely on a central cache to persist the 'refresh_tokens', the refresh token is encrypted and added as a cookie using *crypto/aes*. Naturally the key must be the same if your running behind a load balancer etc. The key length should either 16 or 32 bytes depending or whether you want AES-128 or AES-256.
diff --git a/config.go b/config.go
index 239109f30bd341d7901d12c2c2d7333b3ddb1823..c43bb4b048722780fb478353ebaf7715d28b1fb5 100644
--- a/config.go
+++ b/config.go
@@ -19,6 +19,7 @@ import (
 	"errors"
 	"fmt"
 	"net/url"
+	"os"
 	"regexp"
 	"strings"
 	"time"
@@ -26,6 +27,12 @@ import (
 
 // newDefaultConfig returns a initialized config
 func newDefaultConfig() *Config {
+	var hostnames []string
+	if name, err := os.Hostname(); err == nil {
+		hostnames = append(hostnames, name)
+	}
+	hostnames = append(hostnames, []string{"localhost", "127.0.0.1"}...)
+
 	return &Config{
 		AccessTokenDuration:         time.Duration(720) * time.Hour,
 		CookieAccessName:            "kc-access",
@@ -35,6 +42,8 @@ func newDefaultConfig() *Config {
 		EnableDefaultDeny:           true,
 		EnableSessionCookies:        true,
 		EnableTokenHeader:           true,
+		SelfSignedTLSHostnames:      hostnames,
+		SelfSignedTLSExpiration:     3 * time.Hour,
 		Headers:                     make(map[string]string),
 		LetsEncryptCacheDir:         "./cache/",
 		MatchClaims:                 make(map[string]string),
diff --git a/doc.go b/doc.go
index 75fc6cb69844d091dc82e5eea6bd01e8f212f1ab..cb939d3f0461a1af4075a502e334b6ede32f32e2 100644
--- a/doc.go
+++ b/doc.go
@@ -35,7 +35,7 @@ var (
 
 const (
 	prog        = "keycloak-proxy"
-	author      = "Rohith"
+	author      = "Rohith Jayawardene"
 	email       = "gambol99@gmail.com"
 	description = "is a proxy using the keycloak service for auth and authorization"
 
@@ -183,6 +183,13 @@ type Config struct {
 	// ResponseHeader is a map of response headers to add to the response
 	ResponseHeaders map[string]string `json:"response-headers" yaml:"response-headers" usage:"custom headers to added to the http response key=value"`
 
+	// EnableSelfSignedTLS indicates we should create a self-signed ceritificate for the service
+	EnabledSelfSignedTLS bool `json:"enable-self-signed-tls" yaml:"enable-self-signed-tls" usage:"create self signed certificates for the proxy" env:"ENABLE_SELF_SIGNED_TLS"`
+	// SelfSignedTLSHostnames is the list of hostnames to place on the certificate
+	SelfSignedTLSHostnames []string `json:"self-signed-tls-hostnames" yaml:"self-signed-tls-hostnames" usage:"a list of hostnames to place on the self-signed certificate"`
+	// SelfSignedTLSExpiration is the expiration time of the tls certificate before rotation occurs
+	SelfSignedTLSExpiration time.Duration `json:"self-signed-tls-expiration" yaml:"self-signed-tls-expiration" usage:"the expiration of the certificate before rotation"`
+
 	// EnableRequestID indicates the proxy should add request id if none if found
 	EnableRequestID bool `json:"enable-request-id" yaml:"enable-request-id" usage:"indicates we should add a request id if none found" env:"ENABLE_REQUEST_ID"`
 	// EnableLogoutRedirect indicates we should redirect to the identity provider for logging out
diff --git a/rotation.go b/rotation.go
index 33c535867636a58c66f259ca87d4d903c24192cb..91a2c7a29eba827646b49c7273f496272678e2c8 100644
--- a/rotation.go
+++ b/rotation.go
@@ -44,7 +44,7 @@ func newCertificateRotator(cert, key string, log *zap.Logger) (*certificationRot
 	if err != nil {
 		return nil, err
 	}
-	// step: are we watching the files for changes?
+	// @step: are we watching the files for changes?
 	return &certificationRotation{
 		certificate:     certificate,
 		certificateFile: cert,
diff --git a/self_signed.go b/self_signed.go
new file mode 100644
index 0000000000000000000000000000000000000000..6bc409769079d60514879e55e2aac41424d58595
--- /dev/null
+++ b/self_signed.go
@@ -0,0 +1,140 @@
+/*
+Copyright 2018 All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package main
+
+import (
+	"context"
+	"crypto/rand"
+	"crypto/rsa"
+	"crypto/tls"
+	"errors"
+	"sync"
+	"time"
+
+	"go.uber.org/zap"
+)
+
+type selfSignedCertificate struct {
+	sync.RWMutex
+	// certificate holds the current issuing certificate
+	certificate tls.Certificate
+	// expiration is the certificate expiration
+	expiration time.Duration
+	// hostnames is the list of host names on the certificate
+	hostnames []string
+	// privateKey is the rsa private key
+	privateKey *rsa.PrivateKey
+	// the logger for this service
+	log *zap.Logger
+	// stopCh is a channel to close off the rotation
+	cancel context.CancelFunc
+}
+
+// newSelfSignedCertificate creates and returns a self signed certificate manager
+func newSelfSignedCertificate(hostnames []string, expiry time.Duration, log *zap.Logger) (*selfSignedCertificate, error) {
+	if len(hostnames) <= 0 {
+		return nil, errors.New("no hostnames specified")
+	}
+	if expiry < 5*time.Minute {
+		return nil, errors.New("expiration must be greater then 5 minutes")
+	}
+
+	// @step: generate a certificate pair
+	log.Info("generating a private key for self-signed certificate", zap.String("common_name", hostnames[0]))
+
+	key, err := rsa.GenerateKey(rand.Reader, 2048)
+	if err != nil {
+		return nil, err
+	}
+
+	// @step: create an initial certificate
+	certificate, err := createCertificate(key, hostnames, expiry)
+	if err != nil {
+		return nil, err
+	}
+
+	// @step: create a context to run under
+	ctx, cancel := context.WithCancel(context.Background())
+
+	svc := &selfSignedCertificate{
+		certificate: certificate,
+		expiration:  expiry,
+		hostnames:   hostnames,
+		log:         log,
+		privateKey:  key,
+		cancel:      cancel,
+	}
+
+	if err := svc.rotate(ctx); err != nil {
+		return nil, err
+	}
+
+	return svc, nil
+}
+
+// rotate is responsible for rotation the certificate
+func (c *selfSignedCertificate) rotate(ctx context.Context) error {
+	go func() {
+		c.log.Info("starting the self-signed certificate rotation",
+			zap.Duration("expiration", c.expiration))
+
+		for {
+			expires := time.Now().Add(c.expiration).Add(-5 * time.Minute)
+			ticker := expires.Sub(time.Now())
+
+			select {
+			case <-ctx.Done():
+				return
+			case <-time.After(ticker):
+			}
+			c.log.Info("going to sleep until required for rotation", zap.Time("expires", expires), zap.Duration("duration", expires.Sub(time.Now())))
+
+			// @step: got to sleep until we need to rotate
+			time.Sleep(expires.Sub(time.Now()))
+
+			// @step: create a new certificate for us
+			cert, _ := createCertificate(c.privateKey, c.hostnames, c.expiration)
+			c.log.Info("updating the certificate for server")
+
+			// @step: update the current certificate
+			c.updateCertificate(cert)
+		}
+	}()
+
+	return nil
+}
+
+// close is used to shutdown resources
+func (c *selfSignedCertificate) close() {
+	c.cancel()
+}
+
+// updateCertificate is responsible for update the certificate
+func (c *selfSignedCertificate) updateCertificate(cert tls.Certificate) {
+	c.Lock()
+	defer c.Unlock()
+
+	c.certificate = cert
+}
+
+// GetCertificate is responsible for retrieving
+func (c *selfSignedCertificate) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
+	c.RLock()
+	defer c.RUnlock()
+
+	return &c.certificate, nil
+}
diff --git a/self_signed_test.go b/self_signed_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..ba4ce5b4344addc140bad9dbbb95dd79510e6460
--- /dev/null
+++ b/self_signed_test.go
@@ -0,0 +1,56 @@
+/*
+Copyright 2018 All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package main
+
+import (
+	"crypto/tls"
+	"testing"
+	"time"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+	"go.uber.org/zap"
+)
+
+func TestNewSelfSignedCertificate(t *testing.T) {
+	c, err := newSelfSignedCertificate([]string{"localhost"}, 5*time.Minute, zap.NewNop())
+	assert.NoError(t, err)
+	assert.NotNil(t, c)
+	c.close()
+}
+
+func TestSelfSignedNoHostnames(t *testing.T) {
+	c, err := newSelfSignedCertificate([]string{}, 5*time.Minute, zap.NewNop())
+	assert.Error(t, err)
+	assert.Nil(t, c)
+}
+
+func TestSelfSignedExpirationBad(t *testing.T) {
+	c, err := newSelfSignedCertificate([]string{"localhost"}, 1*time.Minute, zap.NewNop())
+	assert.Error(t, err)
+	assert.Nil(t, c)
+}
+
+func TestSelfSignedGetCertificate(t *testing.T) {
+	c, err := newSelfSignedCertificate([]string{"localhost"}, 5*time.Minute, zap.NewNop())
+	require.NoError(t, err)
+	require.NotNil(t, c)
+	defer c.close()
+	cert, err := c.GetCertificate(&tls.ClientHelloInfo{})
+	assert.NoError(t, err)
+	assert.NotNil(t, cert)
+}
diff --git a/server.go b/server.go
index 2c710dc565c8568a42453ed58eb2b417ec78fd7e..7f0c1739dabdf83e6110269dc06ee4398e1dbfcd 100644
--- a/server.go
+++ b/server.go
@@ -65,7 +65,6 @@ type oauthProxy struct {
 func init() {
 	time.LoadLocation("UTC")             // ensure all time is in UTC
 	runtime.GOMAXPROCS(runtime.NumCPU()) // set the core
-	// @step: register the instrumentation
 	prometheus.MustRegister(certificateRotationMetric)
 	prometheus.MustRegister(latencyMetric)
 	prometheus.MustRegister(oauthLatencyMetric)
@@ -352,16 +351,18 @@ func (r *oauthProxy) createForwardingProxy() error {
 // Run starts the proxy service
 func (r *oauthProxy) Run() error {
 	listener, err := r.createHTTPListener(listenerConfig{
-		listen:              r.config.Listen,
-		certificate:         r.config.TLSCertificate,
-		privateKey:          r.config.TLSPrivateKey,
 		ca:                  r.config.TLSCaCertificate,
+		certificate:         r.config.TLSCertificate,
 		clientCert:          r.config.TLSClientCertificate,
-		proxyProtocol:       r.config.EnableProxyProtocol,
-		useLetsEncrypt:      r.config.UseLetsEncrypt,
-		letsEncryptCacheDir: r.config.LetsEncryptCacheDir,
 		hostnames:           r.config.Hostnames,
+		letsEncryptCacheDir: r.config.LetsEncryptCacheDir,
+		listen:              r.config.Listen,
+		privateKey:          r.config.TLSPrivateKey,
+		proxyProtocol:       r.config.EnableProxyProtocol,
 		redirectionURL:      r.config.RedirectionURL,
+		useFileTLS:          r.config.TLSPrivateKey != "" && r.config.TLSCertificate != "",
+		useLetsEncryptTLS:   r.config.UseLetsEncrypt,
+		useSelfSignedTLS:    r.config.EnabledSelfSignedTLS,
 	})
 
 	if err != nil {
@@ -416,16 +417,18 @@ func (r *oauthProxy) Run() error {
 
 // listenerConfig encapsulate listener options
 type listenerConfig struct {
-	listen              string   // the interface to bind the listener to
-	certificate         string   // the path to the certificate if any
-	privateKey          string   // the path to the private key if any
 	ca                  string   // the path to a certificate authority
+	certificate         string   // the path to the certificate if any
 	clientCert          string   // the path to a client certificate to use for mutual tls
-	proxyProtocol       bool     // whether to enable proxy protocol on the listen
 	hostnames           []string // list of hostnames the service will respond to
-	redirectionURL      string   // url to redirect to
-	useLetsEncrypt      bool     // whether to use lets encrypt for retrieving ssl certificates
 	letsEncryptCacheDir string   // the path to cache letsencrypt certificates
+	listen              string   // the interface to bind the listener to
+	privateKey          string   // the path to the private key if any
+	proxyProtocol       bool     // whether to enable proxy protocol on the listen
+	redirectionURL      string   // url to redirect to
+	useFileTLS          bool     // indicates we are using certificates from files
+	useLetsEncryptTLS   bool     // indicates we are using letsencrypt
+	useSelfSignedTLS    bool     // indicates we are using the self-signed tls
 }
 
 // ErrHostNotConfigured indicates the hostname was not configured
@@ -460,13 +463,15 @@ func (r *oauthProxy) createHTTPListener(config listenerConfig) (net.Listener, er
 		listener = &proxyproto.Listener{Listener: listener}
 	}
 
-	// does the socket require TLS?
-	if (config.certificate != "" && config.privateKey != "") || config.useLetsEncrypt {
+	// @check if the socket requires TLS
+	if config.useSelfSignedTLS || config.useLetsEncryptTLS || config.useFileTLS {
 		getCertificate := func(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
 			return nil, errors.New("Not configured")
 		}
 
-		if config.useLetsEncrypt {
+		if config.useLetsEncryptTLS {
+			r.log.Info("enabling letsencrypt tls support")
+
 			m := autocert.Manager{
 				Prompt: autocert.AcceptTOS,
 				Cache:  autocert.DirCache(config.letsEncryptCacheDir),
@@ -494,9 +499,21 @@ func (r *oauthProxy) createHTTPListener(config listenerConfig) (net.Listener, er
 			}
 
 			getCertificate = m.GetCertificate
-		} else {
+		}
+
+		if config.useSelfSignedTLS {
+			r.log.Info("enabling self-signed tls support", zap.Duration("expiration", r.config.SelfSignedTLSExpiration))
+
+			rotate, err := newSelfSignedCertificate(r.config.SelfSignedTLSHostnames, r.config.SelfSignedTLSExpiration, r.log)
+			if err != nil {
+				return nil, err
+			}
+			getCertificate = rotate.GetCertificate
+
+		}
+
+		if config.useFileTLS {
 			r.log.Info("tls support enabled", zap.String("certificate", config.certificate), zap.String("private_key", config.privateKey))
-			// creating a certificate rotation
 			rotate, err := newCertificateRotator(config.certificate, config.privateKey, r.log)
 			if err != nil {
 				return nil, err
@@ -510,13 +527,13 @@ func (r *oauthProxy) createHTTPListener(config listenerConfig) (net.Listener, er
 		}
 
 		tlsConfig := &tls.Config{
-			PreferServerCipherSuites: true,
 			GetCertificate:           getCertificate,
+			PreferServerCipherSuites: true,
 		}
 
 		listener = tls.NewListener(listener, tlsConfig)
 
-		// are we doing mutual tls?
+		// @check if we doing mutual tls
 		if config.clientCert != "" {
 			caCert, err := ioutil.ReadFile(config.clientCert)
 			if err != nil {
diff --git a/utils.go b/utils.go
index a66e616935b0d54e0f8e5f91e37357a234cb95ff..b02b00614ea4fc0c5a39a13cdaa75f2c925f7ac5 100644
--- a/utils.go
+++ b/utils.go
@@ -19,15 +19,19 @@ import (
 	"crypto/aes"
 	"crypto/cipher"
 	"crypto/rand"
+	"crypto/rsa"
 	sha "crypto/sha256"
 	"crypto/tls"
 	"crypto/x509"
+	"crypto/x509/pkix"
 	"encoding/base64"
 	"encoding/json"
+	"encoding/pem"
 	"errors"
 	"fmt"
 	"io"
 	"io/ioutil"
+	"math/big"
 	"net"
 	"net/http"
 	"net/url"
@@ -62,6 +66,52 @@ var (
 	symbolsFilter = regexp.MustCompilePOSIX("[_$><\\[\\].,\\+-/'%^&*()!\\\\]+")
 )
 
+// createCertificate is responsible for creating a certificate
+func createCertificate(key *rsa.PrivateKey, hostnames []string, expire time.Duration) (tls.Certificate, error) {
+	// @step: create a serial for the certificate
+	serial, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
+	if err != nil {
+		return tls.Certificate{}, err
+	}
+
+	template := x509.Certificate{
+		BasicConstraintsValid: true,
+		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
+		IsCA:                  false,
+		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
+		NotAfter:              time.Now().Add(expire),
+		NotBefore:             time.Now().Add(-30 * time.Second),
+		PublicKeyAlgorithm:    x509.ECDSA,
+		SerialNumber:          serial,
+		SignatureAlgorithm:    x509.SHA512WithRSA,
+		Subject: pkix.Name{
+			CommonName:   hostnames[0],
+			Organization: []string{"Keycloak Proxy"},
+		},
+	}
+
+	// @step: add the hostnames to the certificate template
+	if len(hostnames) > 1 {
+		for _, x := range hostnames[1:] {
+			if ip := net.ParseIP(x); ip != nil {
+				template.IPAddresses = append(template.IPAddresses, ip)
+			} else {
+				template.DNSNames = append(template.DNSNames, x)
+			}
+		}
+	}
+
+	// @step: create the certificate
+	cert, err := x509.CreateCertificate(rand.Reader, &template, &template, &key.PublicKey, key)
+	if err != nil {
+		return tls.Certificate{}, err
+	}
+	certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert})
+	keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)})
+
+	return tls.X509KeyPair(certPEM, keyPEM)
+}
+
 // getRequestHostURL returns the hostname from the request
 func getRequestHostURL(r *http.Request) string {
 	hostname := r.Host
diff --git a/utils_test.go b/utils_test.go
index f133532b154e154f5470fa8331420159de8a9db2..a55917ff16f24a85b67b0a4007f5e1ddd4fbc8a1 100644
--- a/utils_test.go
+++ b/utils_test.go
@@ -17,6 +17,7 @@ package main
 
 import (
 	"bytes"
+	"crypto/tls"
 	"fmt"
 	"io/ioutil"
 	"net/http"
@@ -65,6 +66,51 @@ func TestDecodeKeyPairs(t *testing.T) {
 	}
 }
 
+func TestGetRequestHostURL(t *testing.T) {
+	cs := []struct {
+		Expected   string
+		HostHeader string
+		Hostname   string
+		TLS        *tls.ConnectionState
+	}{
+		{
+			Expected: "http://www.test.com",
+			Hostname: "www.test.com",
+		},
+		{
+			Expected: "http://",
+		},
+		{
+			Expected:   "http://www.override.com",
+			HostHeader: "www.override.com",
+			Hostname:   "www.test.com",
+		},
+		{
+			Expected: "https://www.test.com",
+			Hostname: "www.test.com",
+			TLS:      &tls.ConnectionState{},
+		},
+		{
+			Expected:   "https://www.override.com",
+			HostHeader: "www.override.com",
+			Hostname:   "www.test.com",
+			TLS:        &tls.ConnectionState{},
+		},
+	}
+	for i, c := range cs {
+		request := &http.Request{
+			Method: http.MethodGet,
+			Host:   c.Hostname,
+			TLS:    c.TLS,
+		}
+		if c.HostHeader != "" {
+			request.Header = make(http.Header, 0)
+			request.Header.Set("X-Forwarded-Host", c.HostHeader)
+		}
+		assert.Equal(t, c.Expected, getRequestHostURL(request), "case %d, expected: %s, got: %s", i, c.Expected, getRequestHostURL(request))
+	}
+}
+
 func BenchmarkUUID(b *testing.B) {
 	for n := 0; n < b.N; n++ {
 		s := uuid.NewV1()