@@ -20,6 +20,7 @@ endpoints declared in an OpenAPI 3.1.x specification. Handlers are pure function
2020- [ JSON mapping] ( #json-mapping )
2121- [ Body parsers and response writers] ( #body-parsers-and-response-writers )
2222- [ Server configuration] ( #server-configuration )
23+ - [ HTTPS] ( #https )
2324- [ Interceptors and response decorators] ( #interceptors-and-response-decorators )
2425- [ After-response hooks] ( #after-response-hooks )
2526- [ Security] ( #security )
@@ -326,6 +327,83 @@ OpenApiServer.builder()
326327 .build();
327328```
328329
330+ ### HTTPS
331+
332+ Point the builder at a PEM certificate chain and a PEM PKCS #8 private key:
333+
334+ ``` java
335+ import java.nio.file.Path ;
336+
337+ var server = OpenApiServer . builder()
338+ .spec(spec)
339+ .handlers(handlers)
340+ .https(
341+ Path . of(" /etc/letsencrypt/live/example.com/fullchain.pem" ),
342+ Path . of(" /etc/letsencrypt/live/example.com/privkey.pem" ))
343+ .build();
344+ ```
345+
346+ certbot / Let's Encrypt write exactly these two files to
347+ ` /etc/letsencrypt/live/<domain>/ ` : ` fullchain.pem ` (your certificate + the
348+ issuing intermediates, concatenated PEM) and ` privkey.pem ` (unencrypted PKCS #8 ).
349+ No conversion to PKCS12 / JKS is needed; the library parses the PEM directly
350+ using JDK APIs only.
351+
352+ Both RSA and EC (P-256) private keys are accepted; the algorithm is detected
353+ automatically.
354+
355+ ** Deployment.** Don't bake ` privkey.pem ` into your container image — you
356+ lose rotation and leak the key into image layers and registries. Mount the
357+ two PEM files at runtime from a secret manager:
358+
359+ - ** Kubernetes:** [ cert-manager] ( https://cert-manager.io ) writes the
360+ certificate and key into a ` Secret ` ; mount it as a volume at the path you
361+ pass to ` .https(...) ` . Renewal is automatic; restart the pod (e.g. via a
362+ rolling deploy keyed off the Secret's revision) to pick up the new cert.
363+ - ** GCP:** Store both files in Secret Manager and project them with the
364+ [ Secret Manager CSI driver] ( https://cloud.google.com/secret-manager/docs/access-control )
365+ or a Workload Identity-bound init container that writes the files to an
366+ ` emptyDir ` shared with the app container.
367+ - ** AWS:** [ Secrets Manager] ( https://docs.aws.amazon.com/secretsmanager/ ) via
368+ the [ AWS Secrets and Configuration Provider] ( https://github.com/aws/secrets-store-csi-driver-provider-aws )
369+ for the CSI driver follows the same pattern.
370+
371+ Whatever the source: mount the volume read-only, give ` privkey.pem ` mode
372+ ` 0400 ` (owner-read only), and ensure the JVM process owns or can read it.
373+
374+ When ` .https(...) ` is set, the default port changes from ` 8080 ` to ` 8443 ` .
375+ ` port(int) ` still overrides explicitly:
376+
377+ ``` java
378+ OpenApiServer . builder()
379+ .spec(spec)
380+ .handlers(handlers)
381+ .https(certChain, privateKey)
382+ .port(443 ) // overrides the 8443 default
383+ .build();
384+ ```
385+
386+ For local development without a real certificate, generate a self-signed pair
387+ with one openssl command:
388+
389+ ``` bash
390+ openssl req -x509 -newkey rsa:2048 -nodes -days 365 \
391+ -keyout privkey.pem -out fullchain.pem \
392+ -subj " /CN=localhost" \
393+ -addext " subjectAltName=DNS:localhost,IP:127.0.0.1"
394+ ```
395+
396+ Clients (browsers, ` curl ` , ` HttpClient ` ) need to trust the resulting certificate
397+ explicitly — it isn't signed by a public CA.
398+
399+ ** Not in this release** (each can land later without breaking the API):
400+
401+ - Encrypted / password-protected private keys
402+ - PKCS12 / JKS keystore inputs
403+ - Certificate hot-reload on renewal (restart the process after ` certbot renew ` )
404+ - TLS protocol / cipher overrides (JDK defaults apply: TLS 1.2 and 1.3)
405+ - Serving HTTP and HTTPS from one ` OpenApiServer ` instance
406+
329407### Graceful shutdown
330408
331409` OpenApiServer ` exposes ` stop(int delaySeconds) ` for explicit shutdown that waits up to the given
0 commit comments