Nginx配置proxy protocol代理獲取真實(shí)ip的全過(guò)程
前言
在現(xiàn)代開(kāi)發(fā)中有很多場(chǎng)景需要拿到用戶的真實(shí)ip,比如安全策略,和地區(qū)熱點(diǎn)信息推送等功能,但是現(xiàn)在代理很多。用戶可能會(huì)通過(guò)代理訪問(wèn)服務(wù),或者黑客攻擊的時(shí)候也會(huì)使用很多肉機(jī)隱藏其真實(shí)ip。接下來(lái)我們就來(lái)一起學(xué)習(xí)一下看怎么才能拿到真實(shí)的ip。
一、PROXY Protocol協(xié)議
PROXY Protocol 是一種由 HAProxy 作者 Willy Tarreau 于 2010 年設(shè)計(jì)的網(wǎng)絡(luò)協(xié)議,旨在解決代理環(huán)境中后端服務(wù)器無(wú)法獲取客戶端真實(shí)信息(如源 IP、端口等)的問(wèn)題。它通過(guò)在 TCP/UDP 連接建立后插入一個(gè)輕量級(jí)頭部,實(shí)現(xiàn)客戶端信息的透?jìng)?,且與上層應(yīng)用協(xié)議無(wú)關(guān)
它有兩個(gè)版本:V1、V2
- v1
- 結(jié)構(gòu): PROXY <協(xié)議類(lèi)型> <源IP> <目標(biāo)IP> <源端口> <目標(biāo)端口>
特點(diǎn):- 人類(lèi)可讀,便于調(diào)試。
- 僅支持 TCP/IPv4/IPv6,無(wú)擴(kuò)展能力。
- 頭部最大長(zhǎng)度 108 字節(jié)(IPv6 場(chǎng)景)
- v2
| 字段 | 長(zhǎng)度 | 說(shuō)明 |
|---|---|---|
| 簽名(Signature) | 12 字節(jié) | 固定值 0x0D0A0D0A000D0A515549540A,標(biāo)識(shí) V2 協(xié)議 |
| 版本/命令(Ver/Cmd) | 1 字節(jié) | 高 4 位為版本號(hào)(0x2),低 4 位為命令(0x1=PROXY;0x0=LOCAL) |
| 地址族/協(xié)議(Fam) | 1 字節(jié) | 高 4 位為地址族(IPv4/IPv6/UNIX),低 4 位為協(xié)議(TCP/UDP) |
| 數(shù)據(jù)長(zhǎng)度(Len) | 2 字節(jié) | 后續(xù)數(shù)據(jù)的長(zhǎng)度(大端序) |
| 地址數(shù)據(jù)(Addr) | 可變 | 根據(jù)地址族存儲(chǔ)源/目標(biāo) IP 和端口 |
| TLV 擴(kuò)展 | 可變 | 類(lèi)型-長(zhǎng)度-值結(jié)構(gòu),傳遞 SSL 證書(shū)、唯一 ID 等擴(kuò)展信息 |
特點(diǎn):
- 高效??:二進(jìn)制解析速度快,適合高并發(fā)場(chǎng)景
- 可擴(kuò)展??:通過(guò) TLV 支持 SSL 信息(如證書(shū) CN、加密算法)、唯一連接 ID 等
- 多協(xié)議??:支持 UDP、UNIX Socket 等
二、配置方法
現(xiàn)在內(nèi)網(wǎng)測(cè)試有兩臺(tái)機(jī)器:
一臺(tái)70,一臺(tái)80;兩臺(tái)均為L(zhǎng)inux機(jī)器
其中70是用于代理轉(zhuǎn)發(fā),把請(qǐng)求轉(zhuǎn)發(fā)到80機(jī)器上進(jìn)行處理
80就是部署程序的機(jī)器了

代理服務(wù)器配置
這里也就是指的70服務(wù)器
首先找到nginx對(duì)應(yīng)的配置文件。一般路徑都在:etc目錄下
先切換到管理員權(quán)限:
sudo su
編輯配置文件命令
vi /etc/nginx/nginx.conf
配置內(nèi)容
http模塊代理
http { # 使用 http 模塊處理 HTTP 流量
server {
listen 8888 proxy_protocol; # 啟用 PROXY Protocol 解析
server_name localhost;
location / {
proxy_pass http://192.168.1.80:7890/;#轉(zhuǎn)發(fā)目標(biāo)地址(應(yīng)用程序部署服務(wù)器)
# 傳遞真實(shí) IP 到后端(需后端支持)
proxy_set_header X-Real-IP $proxy_protocol_addr; # 真實(shí)客戶端 IP
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 兼容性頭部
proxy_set_header Host $host;
}
}
log_format tcp_log 'http->[$time_local] $proxy_add_x_forwarded_for → $server_addr ($status)';
access_log /var/log/nginx/tcp_access.log tcp_log;#記錄日志
}
防火墻開(kāi)放端口
sudo ufw allow 8888/tcp
??Stream 模塊?代理
這我配置了一下日志,方便后面測(cè)試查看配置是否起效
stream {
server {
listen 8880 proxy_protocol; # 啟用PROXY協(xié)議解析
proxy_pass 192.168.1.80:7890;#轉(zhuǎn)發(fā)目標(biāo)地址(應(yīng)用程序部署服務(wù)器)
proxy_protocol on; #顯示啟動(dòng)代理協(xié)議透?jìng)?
}
log_format tcp_log 'tcp->[$time_local] $proxy_protocol_addr → $server_addr ($status)';
access_log /var/log/nginx/tcp_access.log tcp_log;#記錄日志
}
防火墻開(kāi)放端口
sudo ufw allow 8880/tcp
注意
http模塊代理和stream模塊代理監(jiān)聽(tīng)的端口不能是相同的
修改配置文件后需要重啟nginx
nginx -s reload
重啟服務(wù)
systemctl restart nginx
在stream 模塊中的這一段配置
listen 8880 proxy_protocol
這里如果設(shè)置了就需要在請(qǐng)求中攜帶PROX頭,下面我會(huì)用C#代碼示例怎么攜帶,這一般是代理服務(wù)器自己會(huì)處理的,如果連接70機(jī)器的服務(wù)沒(méi)有使用代理服務(wù)器這里我們就可以不添加proxy_protocol 這一段 直接使用listen 8880即可,這樣普通的TCP請(qǐng)求也可以直接連接。
測(cè)試配置是否生效
端口檢查
重啟成功后我們可以通過(guò)以下命令查看端口是否正常監(jiān)聽(tīng)
sudo netstat -na | grep LIST | grep 8888

如果查看正常監(jiān)聽(tīng)那就是配置成功了
測(cè)試ip記錄
驗(yàn)證http
linux命令行輸入以下命令(這里測(cè)試用的內(nèi)網(wǎng)85的機(jī)器發(fā)起請(qǐng)求)
curl -H “X-Forwarded-For: 203.0.113.42” 192.168.1.70 8888
日志打印內(nèi)容如下:
http->[22/Jul/2025:09:40:16 +0800] 203.0.113.42, 192.168.1.85 → 192.168.1.73 (200)
驗(yàn)證tcp
這里驗(yàn)證tcp我是采用的.net Core代碼發(fā)起的tcp請(qǐng)求(181是我本機(jī)ip)
代碼如下:
static void Main(string[] args)
{ try
{
// 創(chuàng)建 Socket
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
// 連接到Nginx
IPAddress ipAddress = IPAddress.Parse("192.168.1.73");
IPEndPoint remoteEP = new IPEndPoint(ipAddress, 8880);
socket.Connect(remoteEP);
// 構(gòu)造并發(fā)送PROXY Protocol v1頭部
string proxyHeader = $"PROXY TCP4 {GetLocalIP()} 192.168.1.73 {GetLocalPort(socket)} 8880\r\n";
byte[] proxyHeaderBytes = Encoding.ASCII.GetBytes(proxyHeader);
socket.Send(proxyHeaderBytes);
// 發(fā)送實(shí)際數(shù)據(jù)
string request = "123465";
byte[] requestData = Encoding.ASCII.GetBytes(request);
socket.Send(requestData);
// 輔助函數(shù):獲取本地IP
string GetLocalIP()
{
return Dns.GetHostEntry(Dns.GetHostName())
.AddressList.First(ip => ip.AddressFamily == AddressFamily.InterNetwork)
.ToString();
}
// 輔助函數(shù):獲取本地端口
int GetLocalPort(Socket _socket)
{
return ((IPEndPoint)_socket.LocalEndPoint).Port;
}
}
catch (Exception ex)
{
Console.WriteLine($"發(fā)生錯(cuò)誤: {ex.Message}");
}
}
日志打印內(nèi)容如下
tcp->[22/Jul/2025:10:16:32 +0800] 192.168.1.181 → 192.168.1.73 (200)
這里我們就可以看到我們模擬的proxy頭里的ip了
流程如下

注意事項(xiàng)和理解誤區(qū)
對(duì)于用戶的真實(shí)ip獲取需要注意的是,http請(qǐng)求可以從x-forwarded-for里獲取,但是tcp請(qǐng)求就需要請(qǐng)求發(fā)起方攜帶prox頭,不然nginx會(huì)返回400錯(cuò)誤。
應(yīng)用程序機(jī)器配置
程序中可以使用suerpsocket包,SuperSocket 是一個(gè)用于 .NET 的高性能、可擴(kuò)展的套接字服務(wù)器應(yīng)用程序框架。它為構(gòu)建自定義網(wǎng)絡(luò)通信應(yīng)用程序提供了強(qiáng)大的架構(gòu),支持包括 TCP、UDP 和 WebSocket 在內(nèi)的多種協(xié)議。它2.0中還支持ProxyProtocol協(xié)議的支持。
代碼在程序中的位置:SuperSocket.Connection/ConnectionBase

總結(jié)
以上就是Nginx配置proxy protocol代理獲取真實(shí)ip的全過(guò)程的詳細(xì)內(nèi)容,更多關(guān)于Nginx配置proxy protocol獲取真實(shí)ip的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
nginx根據(jù)二級(jí)目錄轉(zhuǎn)發(fā)服務(wù)以及帶/和不帶/的區(qū)別說(shuō)明
Nginx使用proxy_pass進(jìn)行二級(jí)目錄轉(zhuǎn)發(fā)時(shí),配置中的斜杠(/)影響路徑的處理方式:帶斜杠表示絕對(duì)路徑,不帶斜杠表示相對(duì)路徑,具體轉(zhuǎn)發(fā)到后端服務(wù)的URL會(huì)有所不同2024-12-12
nginx配置后訪問(wèn)出現(xiàn)白屏的問(wèn)題解決
本文主要介紹了nginx配置后訪問(wèn)出現(xiàn)白屏2024-06-06
Nginx下ModSecurity模塊安裝的實(shí)現(xiàn)步驟
本文提供了在Nginx 1.28.0上安裝ModSecurity v3的完整教程,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2025-09-09
解決httpd占用80端口導(dǎo)致Nginx啟動(dòng)失敗報(bào)錯(cuò)的解決辦法
今天在建自己小網(wǎng)站時(shí)啟動(dòng)Nginx時(shí),發(fā)現(xiàn)其報(bào)下列錯(cuò)誤,意思是因?yàn)?0端口被占用導(dǎo)致Nginx啟動(dòng)失敗,所以本文小編給大家介紹介紹如何解決解決httpd占用80端口導(dǎo)致Nginx啟動(dòng)不成功報(bào)nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)2023-11-11

