Choosing Between Ballerina HTTP and HTTPS Listeners for Secure Services
Decide when to use Ballerina's HTTP versus HTTPS listener. Compare setup, performance, and security trade-offs, then implement a keystore-backed HTTPS service with practical validation steps.
08 Aug 2025, 11:39 UTC

The Decision: Plaintext or TLS at the Edge
When you expose a Ballerina service, the listener determines whether traffic arrives as plaintext HTTP or encrypted HTTPS. This choice is not a runtime toggle; it is baked into the listener configuration at startup. Pick HTTP for internal development or trusted networks where encryption is handled by an upstream proxy. Pick HTTPS when the service is reachable from untrusted networks, when compliance requires encryption in transit, or when you need server authentication.
Constraints to weigh before writing code:
- Certificate lifecycle: HTTPS listeners require a keystore containing a server certificate and private key. You must plan for renewal, distribution, and trust.
- Performance: TLS adds handshake latency and per-connection CPU overhead. For internal high-throughput pipelines, this overhead may matter.
- Client compatibility: Mutual TLS requires every client to present a valid certificate, which complicates browser-based and third-party integrations.
Supported Options at a Glance
| Option | Setup | Performance | Security | Typical Use-Case |
|---|---|---|---|---|
| HTTP listener | Low | Minimal | None | Internal/dev |
| HTTPS listener | Medium (cert) | Slight overhead | Confidentiality + Integrity | Public APIs |
Trade-offs Explained
HTTP Listener
The HTTP listener binds a port and serves requests without TLS. Setup is trivial: no keystore, no certificate authority, no handshake. The trade-off is that payloads, headers, and credentials traverse the network in cleartext. Any intermediary can read or modify traffic. Use it only when the network path is already trusted or when a sidecar/ingress terminates TLS before forwarding to your Ballerina process.
HTTPS Listener
The HTTPS listener wraps the same HTTP semantics inside a TLS session. Ballerina accepts a PKCS12 or JKS keystore that holds the server certificate and private key. The listener performs the TLS handshake before any HTTP bytes are parsed. This provides confidentiality and integrity, and it allows optional mutual TLS (mTLS) where the client must also present a certificate.
The cost is operational: you must generate or purchase certificates, configure the keystore path and password, and ensure clients trust the certificate chain. Self-signed certificates work for local testing but cause trust errors in production clients unless those clients are explicitly configured to accept them.
Concrete HTTPS Implementation
The following example defines an HTTPS listener on port 9090 using a PKCS12 keystore and exposes a simple resource. This example assumes a recent Ballerina release (Swan Lake or later); always verify exact field names against the http module documentation for your installed version.
import ballerina/http;
listener http:Listener secureListener = new (9090, {
secureSocket: {
key: {
path: "/etc/ballerina/keystore.p12",
password: "changeit"
}
}
});
service /api on secureListener {
resource function get greet() returns json {
return {"greeting": "hello secure world"};
}
}
Save this to service.bal. Run it from the directory containing the file (or your project root) with:
bal run service.bal
Expected check: the console output should indicate that the listener started on an HTTPS port. If the log instead reports a plain HTTP listener, the secureSocket block was not applied.
Validation with curl
To verify the service is reachable over TLS, invoke it from your terminal with a client certificate and key. This is useful when the listener is configured for mutual TLS.
curl -v https://localhost:9090/api/greet --cert client.crt --key client.key
Expected check: a 200 OK response containing the JSON payload {"greeting":"hello secure world"}. The verbose output (-v) should show a TLS handshake before the HTTP request is sent.
Risk: if the server certificate is self-signed, curl will abort with a trust error. For local testing only, you can bypass verification with --insecure, but never use that flag against production endpoints.
Limitations and Operational Notes
- No runtime switch: You cannot change a listener from HTTP to HTTPS (or vice versa) while the service is running. The process must be restarted with the new configuration.
- Keystore format: Ballerina expects a valid PKCS12 or JKS keystore. A corrupted file or wrong password will prevent the listener from starting.
- Mutual TLS complexity: Enabling client certificate verification requires distributing certificates to every client and configuring the listener's truststore. This is powerful but adds significant operational overhead.
- Performance measurement: The TLS overhead is generally slight for typical API workloads, but if you are optimizing for maximum throughput, benchmark your specific payload sizes and connection rates.
Practical Verification Checklist
- Start the service and confirm the log message references HTTPS and the expected port.
- Run
curl -vand inspect the output for a TLS handshake and a200status code. - Check that the response body matches the resource implementation.
- If the handshake is absent, verify that the listener configuration includes
secureSocketand that the keystore path is correct.
By treating listener selection as a deliberate, restart-bound decision rather than a runtime flag, you can align Ballerina's transport security with your deployment environment from the first line of code.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.