How to start HTTPS server in Bun?
Hypertext Transfer Protocol Secure (HTTPS) is an extension of the Hypertext Transfer Protocol (HTTP). It is used for secure communication over a computer network, and is widely used on the Internet. In HTTPS, the communication protocol is encrypted using Transport Layer Security (TLS) or, formerly, Secure Sockets Layer (SSL). The protocol is therefore also referred to as HTTP over TLS, or HTTP over SSL. (From Wiki)
With HTTP/2, HTTPS got more popular because a lot of servers support secure HTTP/2 only.
In this article, we’ll learn how to write an HTTPS server in Bun.
NOTE: Bun supports HTTPS over HTTP/1.1 only. Bun doesn’t support HTTP/2 yet.
A simple hello world server
Bun provides a simple-to-use Bun.serve API that can start a basic HTTP server. The only mandatory attribute is the request handler, which should take a Request object as input and must return a Response object.
The following is the code of a simple hello world server:
Bun.serve({
port: 3000,
fetch(request) {
return new Response("Hello world!");
},
});
Let’s test it out using curl:
curl http://localhost:3000 -v
* Trying 127.0.0.1:3000...
* Connected to localhost (127.0.0.1) port 3000 (#0)
> GET / HTTP/1.1
> Host: localhost:3000
> User-Agent: curl/7.85.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 HM
< content-type: text/plain;charset=utf-8
< Content-Length: 12
<
* Connection #0 to host localhost left intact
Hello world!
The simple hello world server is good. Now let’s convert it to a secure server.
A secure hello world server
Just like the way other runtimes support HTTPS, like Node.js’s https.server or Deno.serve, Bun also supports in the same way. The exact same Bun.serve API can used to start a secure server by providing two additional options:
- certFile: The path to the cert file
- keyFile: The path to the key file
If certFile and keyFile are provided, the same Bun.serve API will automatically start an HTTPS server.
While Bun expects the path of the cert file and key file, both Node.js and Deno expects the cert file and key file itself. That’s a key difference.
The following is the code of a secure hello world server:
Bun.serve({
port: 3000,
fetch(_) {
return new Response("Hello world!");
},
certFile: "./certs/localhost.crt",
keyFile: "./certs/localhost.key",
});
Let’s test it out using curl:
curl https://localhost:3000 -vk
* Trying 127.0.0.1:3000...
* Connected to localhost (127.0.0.1) port 3000 (#0)
* ALPN: offers h2
* ALPN: offers http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_CHACHA20_POLY1305_SHA256
* ALPN: server did not agree on a protocol. Uses default.
* Server certificate:
* subject: C=US; ST=YourState; L=YourCity; O=Example-Certificates; CN=localhost.local
* start date: Oct 21 16:28:58 2019 GMT
* expire date: Sep 27 16:28:58 2118 GMT
* issuer: C=US; CN=Example-Root-CA
* SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
> GET / HTTP/1.1
> Host: localhost:3000
> User-Agent: curl/7.85.0
> Accept: */*
>
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* old SSL session ID is stale, removing
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 HM
< content-type: text/plain;charset=utf-8
< Content-Length: 12
<
* Connection #0 to host localhost left intact
Hello world!
The server works good. I need to use -k option with curl, as the server certificates were self-signed.
Comparison with Node.js and Deno
Just for the comparison, here is the code of an HTTPS server in Node.js and Deno.
Node.js
const https = require("https");
const fs = require("fs");const options = {
key: fs.readFileSync("./certs/localhost.key", "utf8"),
cert: fs.readFileSync("./certs/localhost.crt", "utf8"),
};https.createServer(
options,
(_, response) => {
response.writeHead(200, {
"content-type": "text/plain",
});
response.end("Hello world");
},
).listen(3000);
Deno
Deno.serve((_) => new Response("Hello world!"), {
port: 3000,
cert: Deno.readTextFileSync("./certs/localhost.crt"),
key: Deno.readTextFileSync("./certs/localhost.key")
});
Limitations
At the time of writing, Bun’s HTTPS server has some limitations:
- Bun only supports HTTPS over HTTP/1.1
- Bun doesn’t support HTTP/2 (plain or secure)
- Bun doesn’t support ALPN
As HTTP/2 is very popular, it’s only a matter of time before Bun starts supporting it.
More articles on similar topics can be seen in the magazine: The JS runtimes.