Quay lại danh sách
Cấu hình proxy theo tên miền để chỉ định lưu lượng

Cấu hình proxy theo tên miền để chỉ định lưu lượng

21 tháng 10, 2026

Vì sao cần định tuyến proxy theo tên miền?

Áp proxy cho toàn bộ yêu cầu thường làm tăng độ trễ, tiêu hao băng thông và khiến lỗi của một dịch vụ bên ngoài ảnh hưởng đến mọi API trong ứng dụng. Trong thực tế, nhóm dữ liệu có thể cần residential proxy để lấy giá theo thị trường, nhưng vẫn muốn gọi dịch vụ nội bộ trực tiếp để có độ trễ thấp.

Định tuyến theo tên miền giải quyết chính xác vấn đề này: chỉ một danh sách tên miền được đi qua proxy, còn lưu lượng còn lại sử dụng kết nối trực tiếp. Cách này cũng giảm rủi ro làm lộ tên máy chủ nội bộ và tránh gửi yêu cầu chứa dữ liệu nhạy cảm qua một proxy không cần thiết.

Nguyên tắc xây dựng quy tắc

Hãy tuân theo bốn nguyên tắc sau:

  1. Khớp chính xác tên miền khi cần một đích cụ thể; dùng tiền tố dấu chấm cho các tên miền con, chẳng hạn .partner-data.com.
  2. Luôn ưu tiên quy tắc trực tiếp nếu một tên miền xuất hiện trong cả hai danh sách.
  3. Đánh giá lại quy tắc sau mỗi lần chuyển hướng vì đích mới có thể thuộc một nhóm khác.
  4. Mặc định gửi trực tiếp và chỉ cho phép đi qua proxy những tên miền đã xác định. Cách này an toàn hơn so với chuyển mọi lưu lượng lạ sang proxy.

Ví dụ cấu hình:

PROXY_DOMAINS=api.vendor.vn,.partner-data.com
DIRECT_DOMAINS=localhost,127.0.0.1,.internal.company.vn
RO_PROXY_URL=http://user:password@proxy.example.com:8000

Với cấu hình này, api.vendor.vn và mọi tên miền con của partner-data.com dùng proxy; API nội bộ vẫn kết nối trực tiếp.

Cấu hình định tuyến trong Python

Thư viện requests không tự so sánh tên miền với một danh sách. Ta cần chọn cấu hình proxy trước mỗi yêu cầu và cho phép requests tiếp tục chuyển hướng nếu cần.

import os
from urllib.parse import urljoin, urlsplit
import requests

PROXY_URL = os.environ['RO_PROXY_URL']
PROXY_DOMAINS = [
    value.strip().lower()
    for value in os.environ.get('PROXY_DOMAINS', '').split(',')
    if value.strip()
]
DIRECT_DOMAINS = [
    value.strip().lower()
    for value in os.environ.get(
        'DIRECT_DOMAINS', 'localhost,127.0.0.1'
    ).split(',')
    if value.strip()
]

def domain_match(host, rule):
    host = host.lower().rstrip('.')
    rule = rule.lower().rstrip('.')
    if rule.startswith('.'):
        return host == rule[1:] or host.endswith(rule)
    return host == rule

def pick_proxies(url):
    host = urlsplit(url).hostname or ''
    if any(domain_match(host, rule) for rule in DIRECT_DOMAINS):
        return None
    if any(domain_match(host, rule) for rule in PROXY_DOMAINS):
        return {'http': PROXY_URL, 'https': PROXY_URL}
    return None

def routed_get(url, **options):
    session = requests.Session()
    session.trust_env = False
    current = url

    for _ in range(6):
        response = session.get(
            current,
            proxies=pick_proxies(current),
            allow_redirects=False,
            timeout=20,
            **options
        )
        if response.is_redirect or response.is_permanent_redirect:
            location = response.headers.get('Location')
            if not location:
                return response
            current = urljoin(current, location)
            continue
        return response

    raise requests.TooManyRedirects('Vượt quá số lần chuyển hướng')

Đặt trust_env=False để các biến HTTP_PROXY hoặc HTTPS_PROXY của môi trường không vô tình thay đổi quy tắc. Khi pick_proxies() trả về None, yêu cầu sẽ đi trực tiếp. Nếu tài khoản hoặc mật khẩu chứa ký tự đặc biệt, phải mã hóa chúng trước khi đặt trong URL proxy.

Chia lưu lượng với cURL

cURL không tự đọc quy tắc theo tên miền, nhưng có thể dùng một hàm bao quanh lệnh gọi:

route_curl() {
  local url=$1
  shift
  case "$url" in
    https://api.vendor.vn/*|https://partner-data.com/*|https://*.partner-data.com/*)
      curl --proxy "$RO_PROXY_URL" -- "$url" "$@"
      ;;
    *)
      curl --noproxy '*' -- "$url" "$@"
      ;;
  esac
}

Tham số --noproxy '*' buộc lệnh không dùng proxy mặc định của hệ thống. Khi có nhiều tên miền nội bộ, biến NO_PROXY thuận tiện hơn, nhưng vẫn nên kiểm tra định tuyến trước khi triển khai.

Định tuyến trong Node.js

Với undici, có thể chọn ProxyAgent hoặc Agent trực tiếp cho từng yêu cầu. request() không tự chuyển hướng, vì vậy vòng lặp bên dưới xử lý chuyển hướng và đánh giá lại tên miền đích.

const { Agent, ProxyAgent, request } = require('undici')

const proxyAgent = new ProxyAgent(process.env.RO_PROXY_URL)
const directAgent = new Agent()
const proxyDomains = (process.env.PROXY_DOMAINS || '')
  .split(',').map(value => value.trim().toLowerCase()).filter(Boolean)
const directDomains = (process.env.DIRECT_DOMAINS || '')
  .split(',').map(value => value.trim().toLowerCase()).filter(Boolean)

function matches(host, rule) {
  return rule.startsWith('.')
    ? host === rule.slice(1) || host.endsWith(rule)
    : host === rule
}

async function routedRequest(url) {
  let current = url

  for (let attempt = 0; attempt < 6; attempt += 1) {
    const host = new URL(current).hostname.toLowerCase()
    const isDirect = directDomains.some(rule => matches(host, rule))
    const isProxied = proxyDomains.some(rule => matches(host, rule))
    const dispatcher = !isDirect && isProxied
      ? proxyAgent
      : directAgent

    const response = await request(current, { dispatcher })
    const redirect = [301, 302, 303, 307, 308]
      .includes(response.statusCode)

    if (redirect && response.headers.location) {
      const next = new URL(response.headers.location, current).toString()
      await response.body.dump()
      current = next
      continue
    }
    return response
  }

  throw new Error('Vượt quá số lần chuyển hướng')
}

Khi ứng dụng kết thúc, nên đóng cả hai tác nhân bằng proxyAgent.close() và directAgent.close() để giải phóng kết nối.

Dùng tệp PAC trong trình duyệt

Tệp Proxy Auto Configuration phù hợp khi cần cùng một quy tắc cho nhiều trang hoặc nhiều tác vụ trình duyệt:

function FindProxyForURL(url, host) {
  host = host.toLowerCase()

  if (host === 'localhost' ||
      host === '127.0.0.1' ||
      dnsDomainIs(host, '.internal.company.vn')) {
    return 'DIRECT'
  }

  if (host === 'api.vendor.vn' ||
      host === 'partner-data.com' ||
      shExpMatch(host, '*.partner-data.com')) {
    return 'PROXY proxy.example.com:8080'
  }

  return 'DIRECT'
}

Lưu tệp trên máy cục bộ, cung cấp nó qua một máy chủ HTTP nội bộ rồi trỏ trình duyệt đến URL của tệp PAC. Trong môi trường doanh nghiệp, nên triển khai bằng chính sách quản lý trình duyệt. Không nên nhúng tên đăng nhập và mật khẩu trong tệp PAC; hãy dùng cơ chế xác thực của trình duyệt hoặc tiện ích quản lý proxy.

DNS, bảo mật và chuyển hướng

Với proxy HTTP và kết nối HTTPS qua CONNECT, tên miền đích thường được gửi đến proxy để proxy tự phân giải. Khi dùng SOCKS5, nên chọn chế độ socks5h để DNS cũng được phân giải ở phía proxy.

Tuy nhiên, tên miền được gửi trực tiếp vẫn cần DNS cục bộ. Proxy không thể đồng thời cho phép kết nối trực tiếp và bảo đảm mọi truy vấn DNS đều chạy qua proxy. Nếu chính sách bảo mật yêu cầu DNS tập trung, hãy kết hợp định tuyến với bộ phân giải mã hóa cục bộ hoặc mạng riêng phù hợp.

Proxy không làm mất tác dụng của TLS. Với HTTPS, proxy chủ yếu thấy tên miền, cổng và thời gian kết nối; nội dung vẫn được mã hóa nếu không cài chứng thay thế. Không được tắt kiểm tra chứng thương mại, tránh gửi bí mật trong URL và không ghi toàn bộ chuỗi truy vấn vào nhật ký.

Kiểm thử quy tắc định tuyến

Hãy kiểm tra cả tuyến mặc định lẫn địa chỉ IP thoát ra:

curl --noproxy '*' --silent --show-error \
  'https://api.ipify.org?format=json'

curl --proxy "$RO_PROXY_URL" --silent --show-error \
  'https://api.ipify.org?format=json'

Hai kết quả thường phải khác nhau. Nếu giống nhau, hãy kiểm tra biến môi trường, thứ tự ưu tiên quy tắc và việc proxy có thực sự hỗ trợ kết nối hay không.

Các kiểm thử tự động nên bao gồm:

  • Tên miền chính xác và tên miền con được gửi qua proxy.
  • Tên miên nội bộ được gửi trực tiếp.
  • Tên miền giống nhưng không phải tên miền con, chẳng hạn api.vendor.vn.evil.test, không được khớp.
  • Một chuyển hướng từ tên miền được phép sang tên miền không được phép được đánh giá lại.
  • Proxy trả về 407, 429, 502 hoặc hết thời gian chờ không làm ứng dụng gọi lại vô hạn.

Ghi lại tên miền, tuyến đã chọn, địa chỉ IP thoát ra, mã trạng thái và độ trễ. Không ghi thông tin xác thực hoặc toàn bộ URL. Cuối cùng, chỉ thu thập dữ liệu được phép, tuân thủ điều khoản dịch vụ, giới hạn tốc độ và chính sách của website đích.