Mark Ku's Blog

The Problem

Large-scale web architectures typically use a load balancer or reverse proxy, and the client IP is usually obtained via the X-Forwarded-For header. However, frpc's HTTPS mode can only expose the real client IP through the Proxy Protocol — and Microsoft's web server (Kestrel) does not natively support that protocol. You therefore need Nginx as a middle layer to extract the real client IP.

What Is Proxy Protocol?

When Proxy Protocol is enabled, frpc sends a Proxy Protocol header to the local service immediately after establishing a connection. The local service parses this header to obtain the visitor's real IP. This works for any TCP service that supports the protocol — not just HTTP.

Traditional reverse proxies pass the client IP via the X-Forwarded-For HTTP header, which can be spoofed by a malicious client. Proxy Protocol, on the other hand, captures the IP at the TCP/IP handshake level, making it tamper-resistant and more secure.

Ways frpc Can Obtain the Client IP

  1. HTTP (unencrypted) — the real client IP can be read from X-Forwarded-For.
  2. HTTPS — you must use the Proxy Protocol at the transport layer to obtain the real client IP.

Step 1: Edit frpc.ini and Set proxy_protocol_version to v2

[common]
server_addr = your public frps url 
server_port = 7000
auth_token = you token
pool_count = 10000
proxy_protocol_version = v2

[web]
type = https
local_port = 443
custom_domains = www.markkulab.net
proxy_protocol_version = v2

Step 2: Configure nginx.conf

upstream frp {
	server 34.80.106.95:80;  # 这个是frp_server的内网ip和http监听端口
}

server {
    listen 80;
    server_name www.letgo.com.tw;
    server_tokens off;

    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

server
	{
	listen 80 proxy_protocol;
	listen 443 ssl http2 proxy_protocol;
	listen [::]:443 ssl http2;
	server_name www.letgo.com.tw; # local server ip

	set_real_ip_from 172.31.0.1; # frp client ip
	real_ip_recursive on;
	real_ip_header  proxy_protocol;


	    ssl_certificate     /etc/letsencrypt/live/www.letgo.com.tw/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/www.letgo.com.tw/privkey.pem;
	
	ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;

	ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
	ssl_prefer_server_ciphers on;
	ssl_session_cache shared:SSL:10m;
	ssl_session_timeout 10m;
	add_header Strict-Transport-Security "max-age=31536000";
	error_page 497  https://$host$request_uri;

	location / {
		proxy_set_header Host $host;
		proxy_set_header X-Real-IP $remote_addr;
		proxy_set_header X-Forwarded-For $proxy_protocol_addr;		
		proxy_set_header X-Forwarded-Proto $scheme;
		proxy_pass http://192.168.50.52:8890/; # your local application ip
	}
}

Step 3: Read the Client IP from the X-Forwarded-For Header in Your App

public static string GetUserIp(this HttpContext context)
{
    var ip = context.Request.Headers["X-Forwarded-For"].FirstOrDefault();

    if (!string.IsNullOrWhiteSpace(ip))
    {
    ip = ip.Replace("::ffff:", "");
    }           
          
    return ip;
}

References

Reference 1

Author

Mark Ku

擁有 10+ 年經驗的資深軟體工程師,現為 AI 應用 Builder,專注於大型平台架構與簡化複雜系統設計,從電商系統到訂閱與收費平台,結合 AI Agent、AI 整合與自動化開發,打造高效率且可持續演進的產品技術基礎。Read More

Found this useful?

The author's free tools, daily podcasts and newsletter are all here.

Mark Ku · This article is licensed under CC BY 4.0. Credit the author and link back to the original when reusing it.

Comments

Subscribe to Newsletter

Subscribe to get new posts delivered instantly — never miss a tech share.

By submitting, you agree to receive emails. You can anytime.

Popular Posts

View all
Mark Ku
··602

Oracle Cloud Always Free Tier: Linux Host and Static IP for a $0 Cloud Solution

Oracle Cloud Always Free Tier: Linux Host and Static IP for a $0 Cloud Solution
Mark Ku
··490

Say Goodbye to Postman's Fee Trap! A Hands-on Guide to Bruno, the Open-Source Git-Native API Testing Powerhouse.

Say Goodbye to Postman's Fee Trap! A Hands-on Guide to Bruno, the Open-Source Git-Native API Testing Powerhouse.
Mark Ku
··333

A Free, Open-Source, Notion-like Knowledge Base — A Complete Guide to Deploying and Backing Up Outline Wiki

A Free, Open-Source, Notion-like Knowledge Base — A Complete Guide to Deploying and Backing Up Outline Wiki
Mark Ku
··264

Training Your Own AI Voice: Hardware Requirements, Open-Source Model Comparison, and LoRA Fine-Tuning

Training Your Own AI Voice: Hardware Requirements, Open-Source Model Comparison, and LoRA Fine-Tuning
Mark Ku
··221

Building an Efficient API Management Platform: Deploying Kong Gateway from Scratch - Part 1

Building an Efficient API Management Platform: Deploying Kong Gateway from Scratch - Part 1
Mark Ku
··215

Setting Up Samba on Ubuntu to Share Folders with Windows 11

Setting Up Samba on Ubuntu to Share Folders with Windows 11
Net core 應用程式,透過 Frpc 內網穿透時,取得用戶端真實 IP - Mark Ku's Tech Notes