Test a TLS Endpoint Safely with openssl s_server
You will finish with a disposable TLS listener, a client connection that confirms the negotiated protocol and cipher, and a short method for serving a test response. The examples use the OpenSSL 3 s_server interface documented by the local openssl-s_server(1ssl) manpage. Allow about fifteen minutes. You need a shell, OpenSSL, and permission to create temporary files and listen on a high TCP port.
The route
Jump straight to the step you need, or tick off Done means at the end.
This is a diagnostic server, not a production web server. The listener accepts TLS connections and can print protocol details, return a status page, or serve files from its current directory. It does not replace a configured service with access controls, logging, certificate renewal and a clear shutdown policy.
1. Check which OpenSSL you will run
Start with read-only checks. They do not need elevated privileges:
$ command -v openssl
/home/linuxbrew/.linuxbrew/bin/openssl
$ openssl version
OpenSSL 3.6.1 27 Jan 2026 (Library: OpenSSL 3.6.1 27 Jan 2026)
$ dpkg-query -W -f='${Package} ${Version}\n' openssl
openssl 3.0.13-0ubuntu3.15
The shell on this machine resolves the Homebrew binary, while the installed Ubuntu package and local manpage identify OpenSSL 3.0.13. Keep that distinction in mind when comparing output with another host. Check openssl s_server -help on the exact binary in your deployment before relying on a newer option.
Checkpoint: confirm that s_server lists the options you intend to use, especially -accept, -cert, -key, -naccept and -brief.
2. Create a disposable certificate
A normal TLS server needs a certificate and private key. Make a short-lived, self-signed pair in a temporary directory for local testing:
$ TEST_DIR=$(mktemp -d /tmp/openssl-s-server.XXXXXX)
$ openssl req -x509 -newkey rsa:2048 -nodes \
-keyout "$TEST_DIR/key.pem" \
-out "$TEST_DIR/cert.pem" \
-sha256 -days 1 -subj '/CN=localhost'
.....+...+........+....+.....+.......+........+....
$ chmod 600 "$TEST_DIR/key.pem"
The key is deliberately unencrypted so the test can start without an interactive prompt. Treat it as disposable: do not use this command's output for a public service, and do not put a real private key in a shared temporary directory. The one-day certificate is self-signed, so a client will report a trust warning unless you explicitly provide it as a trust anchor.
Checkpoint: verify that both files exist and that the private key is not readable by other users:
$ test -s "$TEST_DIR/cert.pem" && test -s "$TEST_DIR/key.pem"
$ stat -c '%a %n' "$TEST_DIR/key.pem"
600 /tmp/openssl-s-server.XXXXXX/key.pem
3. Start one bounded TLS listener
Run the server on loopback and an unprivileged port. This is an ordinary command; sudo is not needed for port 14433. The -naccept 1 limit makes the test exit after one connection:
$ openssl s_server \
-accept 127.0.0.1:14433 \
-cert "$TEST_DIR/cert.pem" \
-key "$TEST_DIR/key.pem" \
-naccept 1 -brief
Using default temp DH parameters
ACCEPT
The command stays in the foreground while it listens. The default port is 4433 when neither -port nor -accept is supplied, and the default accept address is all interfaces on that port. Naming loopback explicitly prevents an accidental test service from being reachable over the network. Stop a foreground server with Ctrl-C; if you use a background process during testing, record its process ID and terminate that process when finished.
4. Connect and inspect the handshake
In a second terminal, connect with the matching OpenSSL client. -brief asks for a compact summary, and -servername sends the TLS server name used by many real clients:
$ openssl s_client -connect 127.0.0.1:14433 \
-servername localhost -brief </dev/null
Connecting to 127.0.0.1
depth=0 CN=localhost
verify error:num=18:self-signed certificate
CONNECTION ESTABLISHED
Protocol version: TLSv1.3
Ciphersuite: TLS_AES_256_GCM_SHA384
Peer certificate: CN=localhost
Verification error: self-signed certificate
DONE
The exact cipher, key exchange group and diagnostic formatting can vary with the OpenSSL build. The useful checks are CONNECTION ESTABLISHED, a protocol version, a cipher suite and the expected certificate subject. The self-signed verification message is expected here. It means the handshake completed but the client did not trust the test certificate.
When the client exits, the server reaches its one-connection limit and stops. If it does not, inspect the server terminal for a bind error, then check whether another process already owns the port:
$ ss -ltn 'sport = :14433'
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
5. Return a status page or a test file
For a browser or HTTP client, replace the plain listener mode with -www. It returns an OpenSSL status page containing session parameters:
$ openssl s_server -accept 127.0.0.1:14433 \
-cert "$TEST_DIR/cert.pem" -key "$TEST_DIR/key.pem" -www
$ curl -k https://127.0.0.1:14433/
The -k flag tells curl to accept the self-signed certificate for this test only. Without it, curl should reject the certificate because it is not signed by a trusted CA. The special /renegcert and /reneg paths have server-specific diagnostic behaviour; do not use them as an application endpoint.
To serve files relative to the current directory, use -WWW. This reads files named by the request, so run it from a directory containing only deliberate test content:
$ mkdir -p "$TEST_DIR/site"
$ printf '%s\n' 'temporary TLS test' > "$TEST_DIR/site/index.html"
$ cd "$TEST_DIR/site"
$ openssl s_server -accept 127.0.0.1:14433 \
-cert "$TEST_DIR/cert.pem" -key "$TEST_DIR/key.pem" -WWW
$ curl -k https://127.0.0.1:14433/index.html
This is a service-disrupting and security-sensitive mode if exposed beyond loopback. Stop it with Ctrl-C, change back to your original directory, and remove the temporary directory only after checking that it contains no files you need. Do not point -WWW at a home directory or a document tree containing secrets.
6. Add client certificate verification only when required
-verify DEPTH asks clients for certificates but permits a client without one to continue. -Verify DEPTH requires a client certificate. Neither option supplies trust by itself: use -verifyCAfile, -verifyCApath or -verifyCAstore to identify trusted CA certificates. Add -verify_return_error when a verification failure must close the connection rather than merely being reported for debugging.
Do not add these flags to the basic self-signed test and assume it proves mutual TLS. A meaningful mutual-TLS test needs a test CA, a client certificate signed by that CA, and a client command configured to present it. Keep those test credentials separate from production credentials and delete or revoke them according to your local process.
Done means
- You checked the OpenSSL binary and version selected by your shell.
- You created a short-lived certificate and protected its private key.
- You bound s_server to loopback and a high port.
- A client completed a handshake and displayed the negotiated protocol and cipher.
- You can distinguish an expected self-signed warning from a failed handshake.
- You bounded or stopped the listener and kept test files away from production data.