TIL: OpenSSL can serve HTTPS
TLDR
OpenSSL includes a small TLS server:
openssl s_server \
-accept 8443 \
-cert ./server.pem \
-key ./server.key \
-www
This listens on port 8443 and returns a small status page. Connect using the
hostname or IP address from your certificate.
To serve files instead, replace lowercase -www with uppercase -WWW.
OpenSSL then serves requested files from the current directory, so run it only
where those files are safe to expose.
Why this is useful
OpenSSL already handles certificate inspection and verification. s_server
adds another useful check: it presents the certificate and uses its matching
private key in a real TLS handshake.
If OpenSSL is already installed, this gives you a temporary HTTPS endpoint without setting up Nginx, Caddy, or a small application only for the test. The terminal also shows incoming handshakes and TLS alerts.
I used this while debugging local HTTPS from an Android app. The OpenSSL output showed that the phone reached the TLS handshake but rejected the development certificate. Once the app trusted the development CA, the same endpoint returned the built-in status page. That separated server reachability from client trust with one command.
Useful options
s_server has many switches. These cover the common debugging cases without
reproducing the whole manual:
| Option | When it helps |
|---|---|
-accept [host:]port | Chooses the listening address and port. Omitting the host, as in -accept 8443, does not restrict the listener to loopback. Use -accept 127.0.0.1:8443 when it should stay local to the machine. |
-4 / -6 | Usually unnecessary. Use -4 when you explicitly need IPv4, or -6 when you explicitly need IPv6. |
-cert_chain ./chain.pem | Supplies the intermediate certificates that the server should send with the leaf. This is easy to miss when the leaf is not signed directly by the trusted root. |
-tls1_2 / -tls1_3 | Forces exactly one TLS version. Without either flag, the server negotiates the highest version shared with the client. Use -min_protocol TLSv1.2 when the requirement is “TLS 1.2 or newer.” |
-brief, -state, -msg, -tlsextdebug | Chooses how much connection detail to print: a short summary, state transitions, protocol messages, or received TLS extensions. |
-naccept 1 | Exits after the specified number of connections. Useful for a one-request reproduction or a script. |
The local installation describes its complete option set with:
openssl s_server -help
I checked these options with OpenSSL 3.6.3. OpenSSL builds can expose different switches, so check the local help before copying an option.
What it proves
If s_server starts, OpenSSL loaded the certificate and its matching private
key. If a client receives the status page, that client reached the listener and
completed the TLS handshake.
s_server is a debugging tool, not a production server.