HTTPSServer gives Python developers a direct way to run a small HTTPS server with the standard library. It extends the familiar behavior of HTTPServer by loading a certificate and private key, wrapping connections in TLS, and keeping the request-handler model used by http.server. This is useful for local integrations, webhook testing, classroom demonstrations, client validation, internal utilities, and prototypes that must use HTTPS.
The convenience does not turn http.server into a production platform. It lacks the hardening, traffic management, observability, process supervision, and abuse protection expected from an internet-facing service. The right approach is to use it for controlled development scenarios while understanding certificates, network exposure, request validation, concurrency, and shutdown behavior.
Creating a basic HTTPS server
The constructor receives the listening address, a request-handler class, the certificate path, and the private-key path. Binding to 127.0.0.1 keeps the service on the local machine, which is safer for development.
from http.server import HTTPSServer, SimpleHTTPRequestHandler
server = HTTPSServer(
("127.0.0.1", 8443),
SimpleHTTPRequestHandler,
certfile="cert.pem",
keyfile="key.pem",
)
print("Serving https://127.0.0.1:8443")
server.serve_forever()
Using 0.0.0.0 exposes the port on every network interface. That can be useful inside a test lab, but it should never happen accidentally. Firewalls, container port mappings, and cloud security groups can make a seemingly local service reachable by other systems.
Development certificates
A self-signed certificate is acceptable for a controlled test environment. OpenSSL can generate a temporary certificate and key:
openssl req -x509 -newkey rsa:2048 -nodes \
-keyout key.pem -out cert.pem -days 30 \
-subj "/CN=localhost"
Modern clients validate Subject Alternative Names, so realistic local testing should include the hostnames and addresses clients will use. Tools such as mkcert can create locally trusted certificates. Never commit a real private key to source control, and restrict file permissions so unrelated users cannot read it.
Writing a custom handler
The server accepts connections, while the handler generates responses. By subclassing BaseHTTPRequestHandler, you can implement routes and methods explicitly.
import json
from http.server import BaseHTTPRequestHandler, HTTPSServer
class ApiHandler(BaseHTTPRequestHandler):
def do_GET(self):
if self.path != "/health":
self.send_error(404, "Not found")
return
body = json.dumps({"status": "ok"}).encode("utf-8")
self.send_response(200)
self.send_header("Content-Type", "application/json; charset=utf-8")
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
server = HTTPSServer(
("127.0.0.1", 8443),
ApiHandler,
certfile="cert.pem",
keyfile="key.pem",
)
server.serve_forever()
Send clear status codes and headers, limit accepted input, and avoid returning stack traces or secrets. A handler should treat URLs, headers, and request bodies as untrusted data even in a test environment.
Using ThreadingHTTPSServer
The basic server handles one request at a time. A slow handler can block every other client. ThreadingHTTPSServer uses a thread for each connection and is more responsive during concurrent tests.
from http.server import ThreadingHTTPSServer, SimpleHTTPRequestHandler
server = ThreadingHTTPSServer(
("127.0.0.1", 8443),
SimpleHTTPRequestHandler,
certfile="cert.pem",
keyfile="key.pem",
)
server.serve_forever()
Threading introduces shared-state risks. Protect mutable shared objects, avoid long CPU-heavy work, set timeouts where possible, and keep handlers short. A thread-per-connection design can still be exhausted by too many clients, which is another reason not to expose it as a general-purpose public service.
ALPN configuration
TLS can advertise application protocols with ALPN. Keep the advertised list aligned with what your handler actually supports. Advertising HTTP/2 while serving only HTTP/1.1 semantics creates negotiation failures or confusing behavior. For ordinary http.server handlers, HTTP/1.1 is the predictable choice.
Testing clients safely
For a self-signed development certificate, curl -k is convenient, but it disables certificate verification and should stay limited to local testing:
curl -k https://127.0.0.1:8443/health
A better Python test trusts the specific certificate:
import ssl
import urllib.request
context = ssl.create_default_context(cafile="cert.pem")
with urllib.request.urlopen(
"https://localhost:8443/health",
context=context,
timeout=5,
) as response:
print(response.read().decode("utf-8"))
This preserves identity verification. Global code that disables verification can hide man-in-the-middle problems and may later reach production unnoticed.
Graceful shutdown
Close the server even when the process is interrupted:
try:
server.serve_forever()
except KeyboardInterrupt:
print("Stopping server")
finally:
server.server_close()
Automated tests often start the server in a background thread. Call shutdown() to stop the serving loop, join the thread, and then call server_close(). This prevents leaked sockets and flaky port conflicts.
Security boundaries
Protect the key file, avoid logging authorization headers, constrain the document root, normalize paths, cap request-body sizes, and reject unsupported methods. Do not expose project secrets through SimpleHTTPRequestHandler. If you need authentication, rate limiting, automated certificate renewal, reverse-proxy features, hardened parsing, HTTP/2, or sustained traffic, use a production server and framework.
Consult the official Python http.server documentation and the OWASP TLS guidance for current details and security recommendations.
Appropriate use cases
HTTPSServer is a good fit for local webhook receivers, SDK integration tests, OAuth redirect experiments, TLS client tests, temporary encrypted file sharing on a trusted machine, educational labs, and reproducible bug demonstrations. It is not a replacement for a hardened application server behind a reverse proxy.
Related Academify guides
Continue with contextlib.chdir, secure tarfile extraction, ssl keylog_filename, and zipfile.Path.
Conclusion
Python HTTPSServer makes a common development task much clearer: start a TLS-protected server without manually replacing sockets. Its value is simplicity, especially for tests and demonstrations. Use local binding, trustworthy certificates, small handlers, explicit validation, careful threading, and reliable shutdown. When requirements grow beyond controlled development, move to a production-grade server rather than stretching a teaching-oriented module beyond its design.







