Getting Started
Making your first API request
This tutorial walks you through installing tlsproxy, configuring your first TLS termination endpoint, and making a verified API request through the proxy to a plain-text backend service. By the end, you will have a working proxy that accepts encrypted HTTPS connections from clients and forwards them as unencrypted HTTP to your backend — a common pattern for terminating TLS at the edge without modifying your backend services. If you are setting up tlsproxy for the first time or want a minimal working example before diving into advanced configuration, start here.
Before you begin, make sure you have the following in place:
- Python 3.8 or later — tlsproxy is built on Twisted, which requires a modern Python runtime.
- Twisted framework — Install via pip:
pip install twisted. Check your version withpython -m twisted --version. - pip 21.0 or later — Needed to resolve dependencies correctly.
- A TLS certificate and private key — You can use a self-signed certificate for local testing (see the example below) or a certificate issued by a trusted CA for production.
- A running plain-text backend service — Any HTTP service listening on a local port (e.g.,
localhost:8080) will work. If you do not have one handy, Python's built-in HTTP server is sufficient for verification:python -m http.server 8080. - openssl CLI tool — Used in this tutorial to generate a self-signed certificate and to send a test request. Verify availability with
openssl version. - curl 7.x or later — Used to make test API requests through the proxy.
The steps below get you from a fresh environment to a working TLS proxy in under five minutes.
- Install tlsproxy and its dependencies.
- Generate a self-signed TLS certificate for local testing.
- Write a minimal proxy configuration file.
- Start the proxy.
- Send a test HTTPS request through the proxy.
- Confirm the backend received the forwarded request.
If any step fails, jump to the Troubleshooting section for common causes and fixes.
Follow each step in order. Each step describes what you are doing, the exact command to run, and what a successful result looks like.
Step 1 — Install tlsproxy
Install tlsproxy and its Twisted dependency using pip:
pip install tlsproxy
Success: The command completes without errors, and tlsproxy --version prints a version string to your terminal.
Step 2 — Generate a self-signed TLS certificate
For local development you need a certificate and key pair. This command creates a 2048-bit RSA certificate valid for 365 days:
openssl req -x509 -newkey rsa:2048 -keyout server.key -out server.crt \
-days 365 -nodes -subj "/CN=localhost"
Two files appear in your working directory: server.crt (the certificate) and server.key (the private key). Keep the private key confidential — never commit it to version control.
Success: Both server.crt and server.key exist in your current directory.
Step 3 — Start a plain-text backend service
tlsproxy forwards decrypted traffic to a backend HTTP service. Start Python's built-in server to act as your backend on port 8080:
python -m http.server 8080
Leave this running in a separate terminal. The proxy will forward requests to this address.
Success: The terminal prints Serving HTTP on 0.0.0.0 port 8080.
Step 4 — Write a minimal configuration file
Create a file named tlsproxy.yaml in your working directory with the following content:
listen:
host: 0.0.0.0
port: 8443
tls:
certificate: ./server.crt
key: ./server.key
backend:
host: 127.0.0.1
port: 8080
This configuration tells tlsproxy to:
- Listen on all interfaces on port 8443 for incoming HTTPS connections.
- Use the certificate and key you generated in Step 2 to terminate TLS.
- Forward decrypted traffic to your backend at
127.0.0.1:8080.
Success: The file is saved and contains no syntax errors. You can validate YAML syntax online or with python -c "import yaml; yaml.safe_load(open('tlsproxy.yaml'))" if PyYAML is installed.
Step 5 — Start the proxy
Launch tlsproxy, pointing it at your configuration file:
tlsproxy --config tlsproxy.yaml
Success: The terminal prints a startup message indicating that tlsproxy is listening on port 8443. You should see a line similar to:
TLS proxy listening on 0.0.0.0:8443 → forwarding to 127.0.0.1:8080
Step 6 — Send a test HTTPS request through the proxy
Open a third terminal and use curl to make an HTTPS request to the proxy. The -k flag tells curl to accept your self-signed certificate without verifying it against a trusted CA:
curl -k https://localhost:8443/
Success: curl returns an HTTP 200 response with HTML content served by Python's built-in HTTP server. The backend terminal (from Step 3) logs the incoming request, confirming that tlsproxy successfully terminated TLS and forwarded the connection.
The following examples demonstrate complete request-response cycles through the proxy. Each example builds on the setup from the steps above.
Example 1 — Basic HTTPS GET request
Send a GET request to the root path and display the HTTP response status and headers:
curl -k -i https://localhost:8443/
Expected output:
HTTP/1.1 200 OK
Server: SimpleHTTP/0.6 Python/3.11.0
Date: Mon, 01 Jan 2024 00:00:00 GMT
Content-type: text/html; charset=utf-8
Content-Length: 372
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" ...>
The HTTP/1.1 200 OK line confirms the proxy accepted your encrypted connection, decrypted it, forwarded the request to the backend, received the plain-text response, and returned it to your curl client.
Example 2 — Inspecting the TLS handshake
Use openssl s_client to inspect the TLS certificate the proxy presents during the handshake. This is useful for verifying your certificate configuration before deploying:
openssl s_client -connect localhost:8443 -showcerts </dev/null
Expected output (truncated):
CONNECTED(00000003)
Certificate chain
0 s:CN = localhost
i:CN = localhost
---
Server certificate
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
---
SSL handshake has read 1102 bytes and written 405 bytes
Verification error: self-signed certificate
---
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
The CN = localhost and TLSv1.3 lines confirm that tlsproxy is presenting your certificate and negotiating a modern TLS version. The self-signed certificate error is expected for local development and is not a proxy misconfiguration.
Example 3 — Sending a POST request with a JSON body
If your backend accepts POST requests, you can forward them through the proxy in the same way:
curl -k -X POST https://localhost:8443/api/data \
-H "Content-Type: application/json" \
-d '{"message": "hello from tlsproxy"}'
Expected behavior: tlsproxy decrypts the HTTPS request and forwards the POST body and headers to 127.0.0.1:8080/api/data unchanged. Your backend receives a plain HTTP POST request as if TLS never existed. The response is re-encrypted by the proxy before being returned to the client.
Use the table below to diagnose common failures. Each entry describes the symptom you observe, the most likely cause, and the corrective action.
Symptom: command not found: tlsproxy after installation.
- Likely cause: The Python scripts directory is not in your
PATH. This happens when pip installs into a user-level directory that your shell does not include by default. - Fix: Run
python -m tlsproxy --config tlsproxy.yamlinstead, or add the pip scripts directory to your PATH. On Linux/macOS you can find it withpython -m site --user-baseand append/binto that path.
Symptom: curl: (35) SSL connect error or Connection refused when hitting port 8443.
- Likely cause: The proxy process is not running, or it failed to bind to the port (possibly due to a port conflict or a YAML syntax error in the config file).
- Fix: Check that the tlsproxy process is still running in its terminal. Look for error output on startup. Verify port 8443 is not already in use:
lsof -i :8443(macOS/Linux) ornetstat -ano | findstr 8443(Windows).
Symptom: curl: (60) SSL certificate problem: self signed certificate even when using -k.
- Likely cause: The
-kflag was omitted, or you are using a different curl invocation that does not include it. - Fix: Confirm your curl command includes
-k(or--insecure). Note that-kshould only be used in local development — never in production.
Symptom: The proxy starts but the backend returns Connection refused or the proxy logs a forwarding error.
- Likely cause: Your backend service (e.g.,
python -m http.server 8080) is not running, or it is listening on a different host or port than what is specified intlsproxy.yamlunderbackend. - Fix: Confirm the backend is running (
curl http://localhost:8080/should return a response directly). Verify thehostandportvalues in your config match the backend's actual address.
Symptom: Permission denied when starting the proxy on port 443 or another port below 1024.
- Likely cause: On Linux and macOS, binding to ports below 1024 requires elevated privileges.
- Fix: For local development, use a port above 1024 (e.g., 8443) as shown in this tutorial. For production, use a process manager that grants the capability, or run the proxy behind a system-level port redirect.
Symptom: Error loading certificate or Error loading private key on startup.
- Likely cause: The paths specified in
tls.certificateortls.keyin your config file do not resolve to the actual files, or the files have incorrect permissions. - Fix: Use absolute paths in your config, or ensure you are running tlsproxy from the same directory as your
.crtand.keyfiles. Check file permissions: the key file should be readable by the user running the proxy (chmod 600 server.key).