A high-performance reverse proxy written in Go, designed to forward HTTP/HTTPS requests to backend services through a local proxy (like Proxyman) for traffic inspection without requiring certificate installation on client devices.
- Protocol Support: HTTP/1.1, HTTP/2, and WebSocket
- Flexible Routing: Rule-based routing with powerful matchers
- Hot-Reload: Configuration changes without restart
- Proxy Integration: Seamless integration with local proxies (Proxyman, Charles, etc.)
- HTTPS Tunneling: CONNECT method support for HTTPS traffic
- Structured Logging: JSON and text output with multiple log levels
# Clone the repository
git clone https://github.com/simman/go-forwarder.git
cd go-forwarder
# Build the application
make build
# Install to system
sudo make installgo install github.com/simman/go-forwarder/cmd/forwarder@latest- Copy the example configuration:
cp configs/config.example.yaml configs/config.yaml- Edit the configuration file to match your needs:
server:
addr: ":22222"
logging:
level: info
format: json
default_proxy: "http://127.0.0.1:9091"
services:
- name: app-traffic
handler:
type: http
listener:
type: tcp
forwarder:
nodes:
- name: my-api
addr: api.example.com:443
filter:
host: api.example.com- Start the forwarder:
./bin/forwarder -config configs/config.yaml- Configure your client to use the proxy:
# Set HTTP proxy
export http_proxy=http://localhost:22222
export https_proxy=http://localhost:22222
# Or configure in your mobile device network settings
# HTTP Proxy: <your-machine-ip>
# Port: 22222The matcher rule syntax provides flexible request matching:
| Matcher | Syntax | Description |
|---|---|---|
| Host | Host{example.com} |
Match request host |
| Host (wildcard) | Host{*.example.com} |
Match subdomain wildcard |
| Path | Path{/exact/path} |
Exact path match |
| PathPrefix | PathPrefix{/api} |
Path prefix match |
| Method | Method{GET} or Method{GET,POST} |
HTTP method match |
| Header | Header{X-Key=value} |
Header key-value match |
| HeaderRegex | HeaderRegex{X-Key=pattern.*} |
Header regex match |
| Query | Query{key=value} |
Query parameter match |
Operators:
&&- AND (both conditions must match)||- OR (either condition must match)!- NOT (negate condition)- Parentheses
()for grouping
Examples:
# Simple host matching
filter:
host: api.example.com
# Complex routing rules
matcher:
rule: Host{api.example.com} && PathPrefix{/v1}
matcher:
rule: Host{example.com} && (Method{GET} || Method{POST})
matcher:
rule: Host{api.example.com} && !Path{/health}
matcher:
rule: Host{*.example.com} && Header{X-Client-Type=mobile}server:
addr: ":22222" # Listen address
read_timeout: 30s # Read timeout
write_timeout: 30s # Write timeout
idle_timeout: 120s # Idle connection timeoutlogging:
level: info # debug, info, warn, error
format: json # json, text
output: stdout # stdout, stderr, or file pathservices:
- name: service-name
handler:
type: http # http, tcp
metadata:
sniffing: true
max_body_size: 10mb
listener:
type: tcp
forwarder:
nodes:
- name: node-name
addr: backend.com:443
filter: # Simple filter OR
host: backend.com
matcher: # Complex matcher
rule: Host{backend.com} && PathPrefix{/api}
proxy: "http://127.0.0.1:9091" # Optional proxy override┌─────────────┐
│ Mobile App │
└──────┬──────┘
│ HTTP/HTTPS
▼
┌─────────────────┐
│ Go-Forwarder │
│ :22222 │
└──────┬──────────┘
│ Match Rules
▼
┌─────────────────┐
│ Proxyman │
│ :9091 │
└──────┬──────────┘
│ Forward
▼
┌─────────────────┐
│ Backend Server │
└─────────────────┘
- Client sends request to go-forwarder (
:22222) - Go-forwarder matches request against configured rules
- If matched, forwards to backend through configured proxy (Proxyman)
- If not matched, returns error response
- Response flows back through the chain to client
Go-forwarder supports hot-reload of configuration without restart. Simply modify the configuration file, and the changes will be automatically applied.
# Edit configuration
vim configs/config.yaml
# Changes are automatically detected and applied
# Check logs for reload confirmation# Build for current platform
make build
# Build for all platforms
make build-all
# Run tests
make test
# Run linter
make lintgo-forwarder/
├── cmd/
│ └── forwarder/ # Application entry point
├── internal/
│ ├── config/ # Configuration management
│ ├── server/ # HTTP server and handlers
│ ├── router/ # Routing engine and matchers
│ └── forwarder/ # Request forwarding
├── pkg/
│ └── logger/ # Logging utilities
├── configs/ # Configuration files
└── scripts/ # Build and installation scripts
Forward mobile app traffic through Proxyman for debugging:
services:
- name: mobile-app
forwarder:
nodes:
- name: api-server
addr: api.myapp.com:443
filter:
host: api.myapp.com
proxy: "http://127.0.0.1:9091"Configure mobile device:
- HTTP Proxy:
<your-mac-ip> - Port:
22222
Route specific API endpoints to different backends:
services:
- name: api-routing
forwarder:
nodes:
- name: api-v1
addr: api-v1.example.com:443
matcher:
rule: Host{api.example.com} && PathPrefix{/v1}
- name: api-v2
addr: api-v2.example.com:443
matcher:
rule: Host{api.example.com} && PathPrefix{/v2}Route based on headers, methods, or query parameters:
nodes:
- name: mobile-backend
addr: mobile.api.com:443
matcher:
rule: Header{X-Client-Type=mobile}
- name: web-backend
addr: web.api.com:443
matcher:
rule: Header{X-Client-Type=web}logging:
level: debug
format: text
output: stdoutWhen a request doesn't match any route, go-forwarder returns a JSON error:
{
"error": "no matching route found",
"host": "example.com",
"path": "/api/test",
"method": "GET"
}Ensure your proxy (Proxyman) is running and accessible:
curl -x http://127.0.0.1:9091 https://example.comContributions are welcome! Please feel free to submit a Pull Request.
MIT License
- Inspired by gost configuration format
- Uses Traefik-style matcher syntax
- Built with zerolog for structured logging
For issues, questions, or suggestions, please open an issue on GitHub.