Quick Guide: Deploy frp for Secure Intranet-to-Internet Access
This article explains how to set up frp, a high-performance reverse-proxy written in Go, to expose internal web and SSH services to the public Internet using domain-based virtual hosts, nginx reverse-proxy, and systemd on CentOS, complete with configuration examples and firewall considerations.
Introduction
frpis a high‑performance reverse‑proxy written in Go that enables intranet penetration (NAT traversal) for TCP, UDP, HTTP and HTTPS, allowing a local web service to be exposed to the Internet.
What frp does
By deploying the frp server on a public‑IP node, internal services can be accessed from outside, with features such as:
Expose HTTP/HTTPS services from machines behind firewalls without a public IP.
Virtual‑host support for multiple domains on a single port.
Expose TCP services (e.g., SSH) from an internal network.
frp Overview
frp supports TCP, UDP, HTTP and HTTPS. The latest version at the time of writing is v0.34.2. The official documentation provides full details; this article shows a quick deployment.
How frp works
After
frpcstarts, it connects to
frpsand sends a login request, keeping a persistent connection.
frpscreates a listener for public requests; when a request arrives, it matches it to an existing client connection or creates a new work connection. Traffic is then forwarded between the public side and the client side, and both ends are closed together if either side disconnects.
Architecture
Practical Deployment Example
Example 1 – Access an internal web service via a domain name
Preparation
An internal server (client)
A public server/VPS (server)
A domain name pointing to the public server (e.g.,
rkjh.xyz)
Environment:
CentOS 7.6
nginx 1.16
frp v0.34.0
Step 1 – Start frp server and client to establish a tunnel
frps listens on port
7080(customizable) for external HTTP requests.
frpc forwards internal web ports
8585and
8686to the public side.
Step 2 – Configure nginx as a reverse proxy
Map the sub‑domain
dev.rkjh.xyzto
frps’s HTTP port (7080). Requests such as
a.dev.rkjh.xyzor
b.dev.rkjh.xyzare forwarded to the corresponding internal ports.
Step 3 – frpc processes incoming requests
If the Host header is
a.dev.rkjh.xyz, forward to internal port
8585.
If the Host header is
b.dev.rkjh.xyz, forward to internal port
8686.
Step 4 – Internal web service handles the request and returns a response.
Step 5 – frpc sends the response back through frps to the external client.
Step 6 – Result
Visiting
a.dev.rkjh.xyzreaches
localhost:8585on the internal server.
Visiting
b.dev.rkjh.xyzreaches
localhost:8686.
Domain DNS configuration
Add two A records under
rkjh.xyz:
devand
*.dev, both pointing to the public server’s IP. All sub‑domains of
dev.rkjh.xyzwill resolve to that IP.
Server configuration
Install frp
<code># download to /data
cd /data
wget https://github.com/fatedier/frp/releases/download/v0.34.0/frp_0.34.0_linux_amd64.tar.gz
tar -zxvf frp_0.34.0_linux_amd64.tar.gz
mv frp_0.34.0_linux_amd64 frp
cd frp
# keep only server files (frps, frps.ini, etc.)
</code>Edit frps.ini
<code>[common]
bind_port = 7000
vhost_http_port = 7080
dashboard_port = 7500
dashboard_user = admin
dashboard_pwd = admin
log_file = /data/frp/log/frps.log
log_level = warn
log_max_days = 7
subdomain_host = dev.msh.com
authentication_method = token
authenticate_heartbeats = true
authenticate_new_work_conns = true
token = 12345678_
enable_prometheus = true
allow_ports = 20000-30000
</code>Start frps with systemd
<code>[Unit]
Description=frps service
[Service]
ExecStart=/data/frp/frps -c /data/frp/frps.ini
Restart=always
RestartSec=10
KillSignal=SIGINT
SyslogIdentifier=frp-service
User=root
[Install]
WantedBy=multi-user.target
</code>nginx reverse‑proxy configuration
<code># install nginx
yum -y install epel-release
yum install -y nginx
# /etc/nginx/nginx.conf
server {
listen 80;
server_name *.dev.rkjh.xyz dev.rkjh.xyz;
location / {
proxy_pass http://127.0.0.1:7080;
proxy_set_header Host $host:80;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_connect_timeout 7d;
proxy_send_timeout 7d;
proxy_read_timeout 7d;
}
if ($http_user_agent ~* "bot|spider|curl|wget") {
return 403;
}
}
</code>Open firewall ports
Allow ports 7000 and 7080 in the security group.
Client configuration
Install frp client
<code># download to /data
cd /data
wget https://github.com/fatedier/frp/releases/download/v0.34.0/frp_0.34.0_linux_amd64.tar.gz
tar -zxvf frp_0.34.0_linux_amd64.tar.gz
mv frp_0.34.0_linux_amd64 frp
cd frp
</code>Edit frpc.ini
<code>[common]
server_addr = <your_public_ip>
server_port = 7000
authentication_method = token
authenticate_heartbeats = true
authenticate_new_work_conns = true
token = 12345678_
log_file = /data/frp/log/frps.log
log_level = warn
log_max_days = 7
[web-a]
type = http
local_port = 8585
subdomain = a
use_encryption = true
use_compression = true
[web-b]
type = http
local_port = 8686
subdomain = b
use_encryption = true
use_compression = true
</code>Start frpc with systemd
<code>[Unit]
Description=frp service
[Service]
ExecStart=/data/frp/frpc -c /data/frp/frpc.ini
Restart=always
RestartSec=10
KillSignal=SIGINT
SyslogIdentifier=frp-service
User=root
[Install]
WantedBy=multi-user.target
</code>When the log shows “start proxy success”, the tunnel is established.
Testing
Visit
http://a.dev.msh.com(or
b.dev.msh.com) in a browser; the request is forwarded to the corresponding internal service. The frp dashboard shows connection status.
Example 2 – SSH access to an internal machine
Configure an
sshproxy on the server:
<code># frps.ini
[common]
bind_port = 7000
[ssh]
listen_port = 6000
auth_token = 123456_
</code>Start
frps, then on the client set:
<code># frpc.ini
[common]
server_addr = x.x.x.x
server_port = 7000
auth_token = 123456_
[ssh]
local_port = 22
</code>Start
frpcand connect with:
<code>ssh -p 6000 [email protected]
</code>Ops Development Stories
Maintained by a like‑minded team, covering both operations and development. Topics span Linux ops, DevOps toolchain, Kubernetes containerization, monitoring, log collection, network security, and Python or Go development. Team members: Qiao Ke, wanger, Dong Ge, Su Xin, Hua Zai, Zheng Ge, Teacher Xia.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.