Tại Sao Proxy Xoay Không Đổi IP? Khắc Phục Lỗi Kết Nối TCP Trong Scraping
4 tháng 10, 2026
Bạn đã cấu hình RoProxy xoay IP sau mỗi yêu cầu, nhưng khi kiểm tra lại, mọi request vẫn xuất phát từ cùng một địa chỉ? Đây là một trong những vấn đề khó hiểu nhất đối với các kỹ sư phát triển và data engineer. Khi gặp phải, bạn có hai lựa chọn: tin rằng nhà cung cấp proxy đang gian lận, hoặc tin rằng cấu hình của bạn đang bị lỗi.
Trong hầu hết các trường hợp, lỗi không nằm ở phía nhà cung cấp mà nằm ở tầng vận chuyển (transport layer) của ứng dụng. Cụ thể là cơ chế TCP Keep-Alive.
Cơ chế TCP Keep-Alive và cách proxy xoay hoạt động
Theo mặc định, giao thức HTTP/1.1 (phiên bản phổ biến nhất hiện nay) sử dụng kết nối liên tục (persistent connection). Khi trình duyệt hoặc thư viện HTTP của bạn gửi một yêu cầu, kết nối TCP đến proxy không bị đóng ngay lập tức. Thay vào đó, nó được giữ lại trong một pool để tái sử dụng cho các yêu cầu tiếp theo.
Mục đích là để giảm chi phí. Mỗi lần tạo kết nối mới đòi hỏi một quá trình bắt tay TCP (3-way handshake) và, đối với HTTPS, một quy trình bắt tay TLS phức tạp. Việc tái sử dụng kết nối giúp tiết kiệm thời gian đáng kể, thường là vài chục mili giây cho mỗi request.
Tuy nhiên, với proxy xoay, vấn đề nảy sinh ở đây. Khi bạn bật chế độ xoay IP theo yêu cầu (per-request rotation) trên RoProxy, proxy xác định địa chỉ IP dựa trên phiên kết nối hoặc token xác thực. Nếu ứng dụng của bạn giữ kết nối TCP mở và gửi 100 request qua cùng một kết nối vật lý đó, nhà cung cấp proxy sẽ chỉ thấy một phiên duy nhất. Do đó, họ sẽ gán cùng một địa chỉ IP cho toàn bộ 100 request đó, bất chấp cài đặt xoay IP của bạn.
Nói cách khác, bạn đang trả tiền cho tính năng xoay IP nhưng về mặt kỹ thuật, traffic vẫn đi qua một đường hầm IP duy nhất.
Tại sao điều này phá hỏng chiến lược scraping của bạn
Việc vô tình tái sử dụng kết nối có ba hậu quả trực tiếp:
- Giới hạn tốc độ không được nới lỏng: Nếu trang mục tiêu giới hạn 10 request/phút mỗi IP, bạn sẽ bị chặn ngay tại request thứ 11 thay vì được xoay IP để tiếp tục.
- Dấu vân tay IP tĩnh: Các hệ thống chống bot dựa vào sự thay đổi địa chỉ IP để phát hiện scraper. Nếu IP không đổi, hành vi của bạn trông giống hệt một bot đang tấn công.
- Lãng phí ngân sách: Bạn thanh toán cho một gói xoay IP nhưng nhận lại hiệu quả của gói IP cố định.
Đây là lý do tại sao việc kiểm soát trạng thái kết nối quan trọng ngang bằng với việc chọn đúng loại proxy.
Cách xác minh lỗi này trong script của bạn
Trước khi sửa code, hãy xác nhận rằng đây chính xác là vấn đề bạn gặp phải. Hãy viết một script đơn giản gửi 5 request đến một dịch vụ trả về IP của người dùng (ví dụ: api.ipify.org hoặc tương tự) và in ra địa chỉ IP nhận được.
import requests
for i in range(5):
response = requests.get("http://api.ipify.org", proxies={"http": "http://user:pass@GATEWAY_HOST:HTTP_PORT"})
print(f"Request {i+1}: {response.text.strip()}")
Nếu bạn thấy cùng một địa chỉ IP xuất hiện 5 lần, bạn đang gặp phải vấn đề tái sử dụng kết nối. Lưu ý rằng nếu bạn chạy code này trong một vòng lặp nhanh, khả năng cao là cùng một kết nối được dùng lại. Nếu bạn thấy 5 IP khác nhau, có thể bạn không gặp vấn đề này, hoặc thư viện của bạn đang tự động xử lý nó.
Khắc phục trong Python (requests và httpx)
Để buộc thư viện tạo một kết nối mới cho mỗi yêu cầu, bạn cần tắt Keep-Alive.
Với requests, cách đơn giản nhất là không sử dụng đối tượng Session (vốn được thiết kế để duy trì cookie và kết nối). Thay vào đó, hãy gọi module-level requests.get. Tuy nhiên, cách chắc chắn hơn là gửi header Connection: close, yêu cầu phía máy chủ đóng kết nối sau khi phản hồi.
import requests
headers = {"Connection": "close"}
proxy_url = "http://user:pass@GATEWAY_HOST:HTTP_PORT"
for i in range(5):
r = requests.get("http://api.ipify.org", proxies={"http": proxy_url}, headers=headers)
print(f"Request {i+1}: {r.text.strip()}")
Với httpx, việc quản lý kết nối chặt chẽ hơn. Bạn không nên dùng httpx.Client() trong vùng scope rộng vì nó giữ kết nối mở. Thay vào đó, hãy tạo client và đóng nó, hoặc sử dụng httpx.get() trực tiếp (mode synchronous) để đảm bảo kết nối được giải phóng sau mỗi lần gọi.
Nếu bạn làm việc với urllib3, hãy đảm bảo rằng poolmanager không giữ quá nhiều kết nối. Đặt maxsize=1 cho pool sẽ hạn chế số lượng kết nối duy trì, nhưng header Connection: close vẫn là cách kiểm soát trực tiếp nhất.
Khắc phục trong Node.js (axios và undici)
Trong hệ sinh thái Node.js, đối tượng Agent là chìa khóa để kiểm soát Keep-Alive. Mặc định, axios sử dụng http.Agent với keepAlive: true.
Để tắt nó, bạn cần tạo một agent tùy chỉnh và truyền vào instance của axios:
const axios = require('axios');
const http = require('http');
const https = require('https');
const agent = new http.Agent({ keepAlive: false });
const httpsAgent = new https.Agent({ keepAlive: false });
const client = axios.create({
httpAgent: agent,
httpsAgent: httpsAgent,
proxy: {
host: 'GATEWAY_HOST',
port: 8080,
},
});
async function checkIp() {
for (let i = 0; i < 5; i++) {
const response = await client.get('http://api.ipify.org');
console.log(`Request ${i + 1}:`, response.data);
}
}
checkIp();
Đối với undici (thư viện gốc của Node.js), mỗi lần gọi undici.request() sẽ tạo một kết nối mới trừ khi bạn sử dụng Pool hoặc Dispatcher được chia sẻ. Do đó, hãy đảm bảo bạn không chia sẻ một đối tượng Pool giữa nhiều request nếu muốn xoay IP cho từng request.
Đánh đổi hiệu năng: Khi nào nên giữ Keep-Alive
Tắt Keep-Alive có cái giá của nó. Như đã đề cập, mỗi kết nối mới yêu cầu bắt tay TCP và TLS. Trong các ứng dụng tốc độ cao hoặc khi bạn đang sử dụng chế độ sticky session (phiên dính) để duy trì trạng thái đăng nhập, việc tắt Keep-Alive là phản tác dụng. Nó sẽ làm tăng độ trễ tổng thể và giảm thông lượng (throughput).
Vì vậy, quyết định nên dựa trên chiến lược proxy của bạn:
- Xoay IP mỗi request: Tắt Keep-Alive để đảm bảo mỗi request đi qua một IP mới.
- Sticky Session: Giữ nguyên Keep-Alive để tối ưu tốc độ và duy trì phiên làm việc.
- Load testing: Tùy thuộc vào mục tiêu; nếu mô phỏng người dùng thật, hãy cân nhắc giữ một số kết nối mở.
Kết luận
Proxy xoay là một công cụ mạnh mẽ, nhưng nó không hoạt động trong chân không. Nó phụ thuộc vào cách ứng dụng của bạn quản lý các kết nối mạng ở tầng thấp. Việc bỏ qua cơ chế TCP Keep-Alive là nguyên nhân hàng đầu khiến các chiến lược xoay IP thất bại một cách bí ẩn.
Bằng cách kiểm tra hành vi IP thực tế trong script của mình và điều chỉnh cấu hình agent hoặc header phù hợp, bạn có thể đảm bảo rằng mỗi đồng tiền bỏ ra cho proxy đều mang lại giá trị thực sự. Hãy luôn nhớ: cấu hình xoay IP chỉ hiệu quả khi kết nối mạng cũng được xoay theo đúng ý định của bạn.