Tutorial

Getting Started

Making your first API request


Overview
Making Your First API Request with tlsproxy A step-by-step workflow for connecting through the TLS termination proxy Start Install tlsproxy pip install tlsproxy Generate TLS Certificate openssl or mkcert for local dev Certificate valid? No Yes Configure tlsproxy Set cert, key, backend host & port Start tlsproxy twistd -n tlsproxy --config=... Send HTTPS Request 🎉 Backend Service Plain-text HTTP (localhost)

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.


Prerequisites

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 with python -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.

Quick start

The steps below get you from a fresh environment to a working TLS proxy in under five minutes.

  1. Install tlsproxy and its dependencies.
  2. Generate a self-signed TLS certificate for local testing.
  3. Write a minimal proxy configuration file.
  4. Start the proxy.
  5. Send a test HTTPS request through the proxy.
  6. Confirm the backend received the forwarded request.

If any step fails, jump to the Troubleshooting section for common causes and fixes.


Steps

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.


Examples

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.


Troubleshooting

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.yaml instead, or add the pip scripts directory to your PATH. On Linux/macOS you can find it with python -m site --user-base and append /bin to 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) or netstat -ano | findstr 8443 (Windows).

Symptom: curl: (60) SSL certificate problem: self signed certificate even when using -k.

  • Likely cause: The -k flag 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 -k should 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 in tlsproxy.yaml under backend.
  • Fix: Confirm the backend is running (curl http://localhost:8080/ should return a response directly). Verify the host and port values 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.certificate or tls.key in 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 .crt and .key files. Check file permissions: the key file should be readable by the user running the proxy (chmod 600 server.key).