How to Configure Custom Subdomain Routing with frp's vhost HTTP Support

To configure custom subdomain routing in frp, set the subDomainHost parameter in your server configuration and define the subdomain field in your client proxy settings, ensuring DNS resolves wildcard subdomains to your frps instance.

The fatedier/frp repository enables virtual host (vhost) HTTP support, allowing a single frps server to route requests to different local services based on custom subdomains. By leveraging the SubDomainHost server option and the subdomain client parameter, you can expose services like app.frps.com and api.frps.com without allocating separate ports for each application.

How frp vhost HTTP Routing Works

The Role of SubDomainHost

In pkg/config/v1/server.go, the SubDomainHost field specifies the base domain that frps appends to any client-requested subdomain. When this value is configured (for example, as frps.com), the server automatically accepts HTTP requests for any *.frps.com hostname. This setting is mandatory for custom subdomain functionality; without it, the server rejects subdomain registration attempts.

Subdomain Validation Rules

Before accepting a proxy registration, frps validates the subdomain string according to strict rules defined in pkg/config/v1/validation/proxy.go. The subdomain must be non-empty, cannot contain dots (.) or asterisks (*), and requires that the server configuration includes a valid SubDomainHost. These constraints ensure valid DNS hostname formation and prevent wildcard conflicts during registration.

Virtual Host Listener Registration

When an frpc client connects with a subdomain parameter, the server invokes Muxer.Listen in pkg/util/vhost/vhost.go to register a vhost listener. The muxer stores this listener in an internal router keyed by the tuple <domain, location, httpUser>, where the domain is constructed as <subdomain>.<SubDomainHost>. This registration creates the mapping between the external hostname and the specific proxy connection.

Request Routing and Dispatch

Incoming TCP connections on vhostHTTPPort are processed by Muxer.handle in pkg/util/vhost/vhost.go. The handler extracts the Host header from the HTTP request and calls getListener to retrieve the matching registered domain. The implementation supports wildcard matching patterns (such as *.example.com) and catch-all (*) logic, enabling flexible routing strategies based on the incoming Host header.

Step-by-Step Configuration

1. Configure the frps Server

Enable vhost HTTP support by specifying vhostHTTPPort and define your base domain using subDomainHost.


# frps.toml

bindPort = 7000
vhostHTTPPort = 8080

# vhostHTTPSPort = 8443  # Optional: for HTTPS routing

subDomainHost = "frps.com"

The vhostHTTPPort directs frps to listen for HTTP-based virtual host routing on the specified port, while subDomainHost establishes the base domain for all custom subdomains.

2. Configure the frpc Client

Define an HTTP proxy type and specify the desired subdomain in your client configuration.


# frpc.toml

serverAddr = "your.frps.host"
serverPort = 7000

[[proxies]]
name = "web-service"
type = "http"
localPort = 80
subdomain = "app"  # Results in app.frps.com

When the client connects, frps registers a listener for app.frps.com. For HTTPS services, change type to https and ensure vhostHTTPSPort is enabled on the server.

3. Configure DNS Wildcard Records

Create a wildcard DNS A record pointing to your frps server IP address to route all subdomains to the tunnel endpoint.

*.frps.com    A    203.0.113.10

This record ensures that requests to app.frps.com, api.frps.com, or any other subdomain resolve to your frps instance.

Complete Configuration Example

Server Configuration (frps.toml)

bindPort = 7000
vhostHTTPPort = 8080
vhostHTTPSPort = 8443
subDomainHost = "example.com"

# Optional authentication

auth.method = "token"
auth.token = "your-secret-token"

Client Configuration (frpc.toml)

serverAddr = "203.0.113.10"
serverPort = 7000
auth.method = "token"
auth.token = "your-secret-token"

[[proxies]]
name = "blog"
type = "http"
localIP = "127.0.0.1"
localPort = 8080
subdomain = "blog"  # Accessible at blog.example.com

[[proxies]]
name = "api"
type = "https"
localIP = "127.0.0.1"
localPort = 8443
subdomain = "api"   # Accessible at api.example.com

Request Flow

When a user accesses http://blog.example.com/, the request flows through the frps vhost muxer as follows:

  1. DNS resolves blog.example.com to the frps server IP.
  2. The browser sends an HTTP request with Host: blog.example.com to port 8080.
  3. frps extracts the Host header and queries the muxer registry in pkg/util/vhost/vhost.go.
  4. The muxer matches the request to the proxy registered with subdomain = "blog".
  5. frps forwards the TCP stream to the local service listening on 127.0.0.1:8080 via the established frpc connection.

Summary

  • Enable vhost support on frps by configuring vhostHTTPPort or vhostHTTPSPort and set subDomainHost to your base domain.
  • Define subdomains in frpc proxy configurations using the subdomain parameter, which must adhere to validation rules (no dots or asterisks).
  • Configure DNS with a wildcard record pointing to your frps server to handle all subdomain resolutions.
  • Routing mechanism relies on the vhost muxer in pkg/util/vhost/vhost.go, which matches the HTTP Host header to registered listeners keyed by domain tuples.

Frequently Asked Questions

Can I use HTTPS with custom subdomain routing?

Yes. Enable vhostHTTPSPort in your frps.toml server configuration and set type = "https" in your client proxy definition. The vhost muxer handles TLS connections on the dedicated HTTPS port and routes them based on the Server Name Indication (SNI) or Host header, following the same subdomain matching logic as HTTP.

What happens if two clients register the same subdomain?

The frp server maintains a registry in Muxer.Listen (see pkg/util/vhost/vhost.go) that keys listeners by the domain tuple. Attempting to register a duplicate subdomain typically results in an error or the newer registration overwriting the previous one, depending on the specific version and configuration. Each subdomain should be unique across all connected clients to ensure deterministic routing.

Do I need to restart frps when adding new subdomains?

No. frp supports dynamic configuration for proxy registrations. When a new frpc client connects or an existing client updates its configuration, the server automatically registers or updates the vhost listener without requiring a restart of the frps process.

Can I use nested subdomains like app.test.frps.com?

No. According to the validation logic in pkg/config/v1/validation/proxy.go, subdomain values cannot contain dots (.), which prevents nested subdomain registration. The subdomain must be a single DNS label (e.g., app or test), which is then concatenated with the subDomainHost base domain to form the complete hostname.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →