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:
- DNS resolves
blog.example.comto the frps server IP. - The browser sends an HTTP request with
Host: blog.example.comto port 8080. - frps extracts the Host header and queries the muxer registry in
pkg/util/vhost/vhost.go. - The muxer matches the request to the proxy registered with
subdomain = "blog". - frps forwards the TCP stream to the local service listening on
127.0.0.1:8080via the established frpc connection.
Summary
- Enable vhost support on frps by configuring
vhostHTTPPortorvhostHTTPSPortand setsubDomainHostto your base domain. - Define subdomains in frpc proxy configurations using the
subdomainparameter, 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →