Configuration: forward proxy

This page documents directives for configuring Ferron as an HTTP forward proxy. Ferron accepts requests from clients and forwards them to external destinations. It supports HTTP CONNECT tunneling (for HTTPS/WebSocket) and HTTP/1.x absolute URI forwarding.

forward_proxy⁠#

proxy.example.com {
    forward_proxy {
        allow_domains "example.com" "*.example.com"
        allow_ports 80 443
        deny_ips "127.0.0.0/8" "169.254.169.254/32"

        connect_method
        http_version "1.1"
    }
}
Nested directiveArgumentsDescriptionDefault
allow_domains<string>...Allowed destination domains. Supports * wildcards. If empty, Ferron denies all domains (deny-by-default).none (deny all)
allow_ports<int>...Allowed destination ports.80, 443
deny_ips<CIDR>...Denied destination IP ranges, applied after DNS resolution.Loopback, RFC 1918, link-local, cloud metadata (see below)
connect_method<bool> or bareEnable HTTP CONNECT tunneling. When disabled, Ferron rejects CONNECT requests with 403.true
http_version1.0 or 1.1HTTP version used for upstream connections.1.1

Default denied IP ranges⁠#

When you specify no deny_ips, Ferron denies the following ranges by default:

RangeDescription
127.0.0.0/8IPv4 loopback
::1/128IPv6 loopback
10.0.0.0/8RFC 1918 private network
172.16.0.0/12RFC 1918 private network
192.168.0.0/16RFC 1918 private network
169.254.0.0/16Link-local
100.64.0.0/10Shared address space (RFC 6598)
192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24Documentation ranges (RFC 5737)
fd00::/8IPv6 unique local addresses
169.254.169.254/32Cloud metadata endpoint

Security model⁠#

The forward proxy uses a deny-by-default model:

  1. Domain control: If you do not set allow_domains, Ferron denies all destination domains.
  2. Port control: Ferron permits only explicitly allowed ports (defaults to 80 and 443).
  3. IP blocking: After DNS resolution, Ferron checks the final IP against the deny list.
Note

DNS resolution happens at connect time. Ferron validates the resolved IP against the deny list to prevent DNS rebinding attacks.

Request handling⁠#

CONNECT tunneling⁠#

When a client sends an HTTP CONNECT request, Ferron:

  1. Validates the destination against ACLs (domain, port, IP)
  2. Establishes a TCP connection to the target
  3. Returns 200 Connection Established to the client
  4. Bidirectionally forwards raw TCP data between client and target

HTTP forwarding⁠#

When a client sends an HTTP request with an absolute URI, Ferron:

  1. Validates the destination against ACLs
  2. Connects to the target host
  3. Rewrites the request URI to path-only form
  4. Forwards the request via HTTP/1.1
  5. Returns the upstream response to the client

Ferron supports only the http scheme. It rejects https requests with 400.

Examples⁠#

Basic forward proxy⁠#

proxy.example.com {
    forward_proxy {
        allow_domains "example.com" "*.example.com" "api.service.internal"
        allow_ports 80 443
    }
}

Forward proxy with explicit IP denylist⁠#

proxy.example.com {
    forward_proxy {
        allow_domains "*.corp.example.com"
        allow_ports 80 443 8080
        deny_ips "10.0.0.0/8" "172.16.0.0/12"
    }
}

Forward proxy without CONNECT⁠#

proxy.example.com {
    forward_proxy {
        allow_domains example.com
        allow_ports 80
        connect_method false
    }
}
Note

For HTTPS forwarding, clients must use CONNECT tunneling. Direct https:// URLs in HTTP requests are not supported.

Observability⁠#

Metrics⁠#

MetricTypeAttributesDescription
ferron.forward_proxy.requestsCounterferron.forward_proxy.mode ("connect" or "request"), ferron.forward_proxy.result (outcome), http.response.status_code, error.type (optional)Forward-proxy requests by mode and outcome

Logs⁠#

  • ERROR: Ferron logs this when a CONNECT upgrade fails and produces no upgrade future. It also logs when the backend TCP connection fails, the HTTP/1 handshake fails, or the request to the backend fails. The message includes the target address and error details.

  • WARN: Ferron logs this when the ACL denies a CONNECT or HTTP request by port or domain. It also logs when the CONNECT method is off. It also logs when a client sends a malformed request or uses an unsupported scheme. It also logs when Ferron cannot set TCP_NODELAY, DNS resolution fails, or a CONNECT tunnel encounters an error.

  • INFO: Ferron logs this when a CONNECT tunnel closes normally. The message includes byte counts for each direction.

Structured logs⁠#

Description (summary)LevelAttributes
Forward proxy config errorERRORerror.message (string) : configuration error details
Forward proxy CONNECT upgrade failedERRORforward_proxy.target (string) : target address, error.type (string), error.message (string)
Forward proxy connection to target failedERRORforward_proxy.target (string) : target address, error.type (string), error.message (string)
Forward proxy CONNECT tunnel errorWARNforward_proxy.target (string) : target address, error.type (string), error.message (string)
Forward proxy: upstream connect failedERRORupstream.address (string) : target address, error.type (string), error.message (string)
Forward proxy: HTTP/1 handshake failedERRORerror.type (string), error.message (string)
Forward proxy: request to backend failedERRORerror.type (string), error.message (string)
Forward proxy: port denied by ACLWARNnetwork.destination.port (int) : denied port, error.type (string)
Forward proxy: domain denied by ACLWARNnetwork.destination.name (string) : denied domain, error.type (string)
Forward proxy: DNS resolution failedWARNdns.name (string) : hostname that failed resolution, error.type (string)
Forward proxy: resolved IP deniedWARNdns.name (string) : hostname, error.type (string)
Forward proxy: CONNECT disabledWARNerror.type (string)
Forward proxy: bad CONNECT requestWARNerror.type (string)
Forward proxy: unsupported schemeWARNurl.scheme (string) : the unsupported scheme, error.type (string)
Forward proxy: missing hostWARNerror.type (string)

Access log fields⁠#

The forward proxy module contributes the following field to the HTTP access log line:

FieldTypeDescription
ferron.fproxy.modestringForward proxy mode: tunnel (CONNECT) or proxy (standard).

Trace spans⁠#

The forward proxy stage sets the following attributes on its ferron.stage.forward_proxy span:

AttributeTypeDescription
ferron.fproxy.modestringProxy mode: tunnel (CONNECT) or proxy (standard).
ferron.fproxy.upstreamstringThe upstream host and port.
http.response.status_codeintHTTP status code returned to the client.
error.typestringError type on failure, enabling trace UI highlighting.

Best practices⁠#

ferron doctor reports the following best-practice checks for directives on this page.

  • allow_domains "*": Allowing proxying to any public domain defeats the purpose of a forward proxy. Restrict to destinations your clients actually need.
  • Custom deny_ips without loopback and metadata ranges: Overriding the default deny list without including 127.0.0.0/8, ::1, and 169.254.169.254/32 can expose internal services through the proxy.