How to setup HTTPS web server in Golang with self-signed SSL TLS certificate

Author: Ganesh Velrajan

Last Updated: Sat, Sep 23, 2023

In this tutorial, you’ll create a simple HTTPS web server using Go’s standard net/http library. You’ll also create a self-signed SSL TLS X.509 server certificate using the OpenSSL tool. You’ll then use the certficate to run the Golang HTTPS web server.

Finally, you’ll also learn how to create a simple HTTPS web server with mutual TLS(mTLS) authentication added. This will enforce HTTPS web clients also to provide an SSL Client Certificate to connect with the HTTPS server.

Let’s get started.

Create SSL TLS X.509 certificate

Before you could create the HTTPS server, you need to create a self-signed SSL/TLS X.509 certificate for the HTTPS web server. For this, please refer to the following tutorial:

BastionXP is a free open-source based SSL TLS X.509 certificate management software to automatically generate, renew and manage SSL X.509 certificates for various applications(web server, web clients, database, web apps, workloads, devices) in your orgnaization.

Once you have created the server SSL certificate and key, you can move on to the next section.

Simple Golang HTTPS Server Example

Here’s is a simple Golang HTTPS server example:

// https-server.go
package main
import (
	"crypto/tls"
	"log"
	"net/http"
)
var (
	CertFilePath = "/home/user/server-cert.pem"
	KeyFilePath  = "/home/user/server-key.pem"
)
func httpRequestHandler(w http.ResponseWriter, req *http.Request) {
	w.Write([]byte("Hello,World!\n"))
}
func main() {
	// load tls certificates
	serverTLSCert, err := tls.LoadX509KeyPair(CertFilePath, KeyFilePath)
	if err != nil {
		log.Fatalf("Error loading certificate and key file: %v", err)
	}
	
	tlsConfig := &tls.Config{
		Certificates: []tls.Certificate{serverTLSCert},
	}
	server := http.Server{
		Addr:      ":4443",
		Handler:   http.HandlerFunc(httpRequestHandler),
		TLSConfig: tlsConfig,
	}
	defer server.Close()
	log.Fatal(server.ListenAndServeTLS("", ""))
}

Here we are using the Go’s crypto/tls library to read and load the SSL TLS X.509 certificate and key pair from the files in disk. We then create a TLS config and set the TLS certificate field to the loaded TLS certificate and key pair. Next we create a http.Server instance with the following configuration:

  • The IP address and TCP port where the HTTPS web server would listen. In this example, the HTTPS server would listen on IP address 0.0.0.0 and TCP port 4443.
  • The HTTP request handler function to be invoked when a HTTP request is received from a client. Our simple HTTP handler function httpRequestHandler() responds with a Hello,World! message to the HTTP client.
  • The TLS configuration for the HTTPS server.

We finally, invoke the server.ListenAndServerTLS() method - to make the HTTPS server instance listen on the configured IP address and port specified in the server.Addr field, using the TLS certificates configured in server.TLSConfig field.

Now, if we use the curl tool to make a HTTPS request to the HTTPS server, the httpRequestHanlder() method will be invoked. The curl utility will print the HTTPS response message Hello,World! from the server.


$ curl https://localhost:4443
curl: (60) SSL certificate problem: self signed certificate
More details here: https://curl.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.

We get the above error because the TLS X.509 certificate is a self-signed certificate. It was not generated by a Certificate Authority(CA) trusted by the system. We can request curl to safely ignore this message using the --insecure flag.


$ curl https://localhost:4443 --insecure
Hello,World!

Now that you have a fully functioning HTTPS server, you can move to create a HTTPS client in Go. Please refer to the following tutorial:

Golang HTTPS Server with mutual TLS (mTLS) authentication

Here is a simple HTTPS server with mutual authenticaton TLS (mTLS) setup:

// https-server-mtls.go
package main
import (
	"crypto/tls"
	"crypto/x509"
	"io/ioutil"
	"log"
	"net/http"
)
var (
	CACertFilePath = "/home/user/ca-cert.pem"
	CertFilePath   = "/home/user/server-cert.pem"
	KeyFilePath    = "/home/user/server-key.pem"
)
func httpRequestHandler(w http.ResponseWriter, req *http.Request) {
	w.Write([]byte("Hello,World!\n"))
}
func main() {
	// load tls certificates
	serverTLSCert, err := tls.LoadX509KeyPair(CertFilePath, KeyFilePath)
	if err != nil {
		log.Fatalf("Error loading certificate and key file: %v", err)
	}
	// Configure the server to trust TLS client cert issued by your CA.
	certPool := x509.NewCertPool()
	if caCertPEM, err := ioutil.ReadFile(CACertFilePath); err != nil {
		panic(err)
	} else if ok := certPool.AppendCertsFromPEM(caCertPEM); !ok {
		panic("invalid cert in CA PEM")
	}
	tlsConfig := &tls.Config{
		ClientAuth:   tls.RequireAndVerifyClientCert,
		ClientCAs:    certPool,
		Certificates: []tls.Certificate{serverTLSCert},
	}
	server := http.Server{
		Addr:      ":4443",
		Handler:   http.HandlerFunc(httpRequestHandler),
		TLSConfig: tlsConfig,
	}
	defer server.Close()
	log.Fatal(server.ListenAndServeTLS("", ""))
}

The primary differences between this Go HTTPS web server and the previous one are:

  • Enforcing HTTPS client authentication. A HTTP client that connects without a client TLS X.509 certificate will be rejected. The tls.Connfig.ClientAuth field is set with the flag tls.RequireAndVerifyClientCert to enforce HTTPS client authentication.

  • Making the HTTPS server to trust the Certificate Authority(CA). So that the HTTPS server accepts any HTTPS client connecting with a TLS X.509 client certificate issued by this CA. We create a new certficate pool using x509.NewCertPool(), add the CA certificate to the certificate pool and finally add the certifiicate pool to the tls.Config.

Test HTTPS server with Curl command

Now, let’s test the above HTTPS server, enforcing mutual TLS authentication(mTLS), with the curl utlity. If we run the curl command as we did in the previous example, it will throw an error as shown below:


$ curl https://localhost:4443 --insecure
curl: (56) OpenSSL SSL_read: error:14094412:SSL routines:ssl3_read_bytes:sslv3 alert bad certificate, errno 0

This is because the curl command is not using any client TLS certificate to connect with the mTLS based HTTPS server. We need to pass the client certificate and key as arguments to the curl command as shown below:


$ curl https://localhost:4443 --insecure --cert client-cert.pem --key client-key.pem
Hello,World!

Now that you have a fully functioning mutual TLS HTTPS server, you can move on to create a mTLS HTTPS client with client certificate in Go. Please refer to the following tutorial: HTTPS Client (mTLS Client) Example in Golang using Self-Signed SSL/TLS Client Certificate

Automate SSL Certificate Management using BastionXP

BastionXP PKI/CA simplifies and automates the management of SSH and SSL/TLS X.509 certificates(both server and client certificates) without affecting the end user workflow.

BastionXP automatically generates new server and client SSH, SSL/TLS X.509 certificates after an end user successfully authenticates via a 2FA (two-factor authentication) enabled SSO provider such as Microsoft Azure 365, Google G-Suite, Okta, GitHub, Keycloak or any SSO provider.

BastionXP CA simplifies enabling mutual TLS authentication for applications, virutal appliances, microservices and workloads in your organization.

BastionXP PKI/CA with built-in Role Based Access Control (RBAC), issues short-lived SSH and SSL/TLS client certificates to end users, so that IT admins have granular control over who can access what resources in your organization and for how long.

Start Your Free Trial Now

Try BastionXP for free with no commitments. No credit card required.