Simple HTTP/HTTPS forward proxy server in Python (stdlib only, no external dependencies). Designed for use in local networks — e.g., to route traffic from another application through a machine with a specific IP address.
- HTTP proxy — proxying GET, POST, PUT, DELETE, HEAD, OPTIONS, PATCH requests
- HTTPS CONNECT tunnel — transparent tunneling of TLS connections
- Multithreading — each incoming connection is handled in a separate thread
- Configuration via environment variables
- Logging — timestamp, method, target host, HTTP status
- Graceful shutdown — clean termination on
Ctrl+C/SIGTERM - Docker-ready —
Dockerfile+docker-compose.ymlincluded - Railway-ready —
railway.tomlincluded for one-click deploy
# Clone the repository
git clone <repo-url> && cd proxy
# (optional) copy and edit .env
cp .env.example .env
# Start
docker compose up -d --buildThe proxy will be available on port 1111 (or whatever is set in PROXY_PORT).
python3 proxy_server.pyOr with a custom port:
PROXY_PORT=2222 python3 proxy_server.pyOn Windows without Docker — only Python 3 is required.
# Start with default settings (port 1111)
.\start_proxy.ps1
# Custom port
.\start_proxy.ps1 -Port 2222
# Disable logging
.\start_proxy.ps1 -Port 1111 -NoLogOr without the script:
$env:PROXY_PORT = "1111"
python proxy_server.pyNote: if Windows Firewall blocks incoming connections, add a rule:
New-NetFirewallRule -DisplayName "Forward Proxy" -Direction Inbound -Protocol TCP -LocalPort 1111 -Action Allow
- Push this repo to GitHub
- In Railway dashboard: New Project → Deploy from GitHub repo
- Go to Settings → Networking → TCP Proxy and enable it
- Set variable:
PROXY_PORT=${{PORT}} - Use the provided TCP address in your client:
INSTAGRAM_PROXY=http://region.proxy.rlwy.net:12345All settings are controlled via environment variables:
On the machine running the proxy:
# Linux
hostname -I
# macOS
ipconfig getifaddr en0Let's say the IP is 192.168.1.50.
In the .env file of your project, add:
INSTAGRAM_PROXY=http://192.168.1.50:1111import os
import requests
proxy_url = os.environ["INSTAGRAM_PROXY"]
session = requests.Session()
session.proxies = {
"http": proxy_url,
"https": proxy_url,
}
# HTTP request through proxy
response = session.get("http://httpbin.org/ip")
print(response.json())
# HTTPS request through proxy (CONNECT tunnel)
response = session.get("https://api.instagram.com/")
print(response.status_code)# Proxy on another machine in the local network
INSTAGRAM_PROXY=http://192.168.1.50:1111
# Proxy on the same machine (for testing)
INSTAGRAM_PROXY=http://127.0.0.1:1111
# Proxy by hostname
INSTAGRAM_PROXY=http://my-proxy-server:1111
# Proxy on a custom port
INSTAGRAM_PROXY=http://192.168.1.50:2222# HTTP through proxy
curl -x http://localhost:1111 http://httpbin.org/ip
# HTTPS through proxy
curl -x http://localhost:1111 https://httpbin.org/ipIf everything works, both requests will return a JSON with the proxy server's IP address.
import requests
proxies = {"http": "http://localhost:1111", "https": "http://localhost:1111"}
print(requests.get("https://httpbin.org/ip", proxies=proxies).json())When PROXY_LOG=true (default), stdout will show entries like:
2026-03-13 12:00:01 Proxy listening on 0.0.0.0:1111
2026-03-13 12:00:05 GET http://httpbin.org/ip 200
2026-03-13 12:00:07 CONNECT api.instagram.com:443 200
If you want to add the proxy as a service in another project's docker-compose.yml:
services:
# ... your services ...
proxy:
build: ./proxy # path to the directory with proxy_server.py and Dockerfile
container_name: forward-proxy
restart: unless-stopped
ports:
- "1111:1111"
environment:
- PROXY_PORT=1111
- PROXY_LOG=trueFrom other containers in the same Docker network, the proxy is available at:
INSTAGRAM_PROXY=http://proxy:1111(where proxy is the service name in docker-compose)
If the proxy runs in Docker and the client is on the host machine:
INSTAGRAM_PROXY=http://localhost:1111If both the proxy and client are in Docker (same docker-compose network):
INSTAGRAM_PROXY=http://forward-proxy:1111
# or by service name:
INSTAGRAM_PROXY=http://proxy:1111If the proxy is in Docker and the client is on another machine in the network:
# Use the IP of the host machine running Docker
INSTAGRAM_PROXY=http://192.168.1.50:1111- Check that the container is running:
docker compose ps - Check logs:
docker compose logs proxy - Verify the port is open:
curl http://localhost:1111(expect a 400 error — that's normal, it means the proxy is working)
- Make sure the firewall is not blocking port
1111 - Check that
PROXY_BIND=0.0.0.0(not127.0.0.1) - Test connectivity:
telnet <proxy-host> 1111
- Default connection timeout is 60 seconds
- If the target server doesn't respond within 60 seconds, the connection will be closed
- For long-lived connections, you can change
TIMEOUTinproxy_server.py
# Find what's using the port
lsof -i :1111
# Use a different port
PROXY_PORT=2222 docker compose up -d --build┌──────────┐ HTTP GET ┌─────────────┐ HTTP GET ┌──────────────┐
│ Client │ ──────────────────▶ │ Proxy Server │ ──────────────────▶ │ Origin Server│
│ (Python │ ◀────────────────── │ (port 1111) │ ◀────────────────── │ │
│ requests)│ Response └─────────────┘ Response └──────────────┘
└──────────┘
┌──────────┐ CONNECT host:443 ┌─────────────┐ TCP connect ┌──────────────┐
│ Client │ ──────────────────▶ │ Proxy Server │ ──────────────────▶ │ Origin Server│
│ │ 200 Established │ │ │ (TLS) │
│ │ ◀────────────────── │ │ │ │
│ │ ◀═══ TLS tunnel ═══▶ │ │ ◀═══ TCP relay ═══▶ │ │
└──────────┘ └─────────────┘ └──────────────┘
- HTTP proxy: the client sends a full URL in the request → the proxy parses the URL, connects to the origin server, forwards the request and response
- HTTPS CONNECT: the client sends
CONNECT host:443→ the proxy establishes a TCP connection to the target server → responds with200→ starts a bidirectional relay (client and server communicate directly through the tunnel)
