返回博客

为系统管理员和DevOps设置curl和wget代理:实用示例

完整的curl和wget代理设置指南,包含实际代码示例,支持SOCKS5,认证以及CI/CD管道中的自动化。

📅2026年4月3日
```html

如果您管理服务器、编写自动化脚本或在企业基础设施中部署应用程序——您迟早会遇到需要通过代理转发 curl 或 wget 流量的情况。这可能是企业代理、绕过地理限制下载包,或在对外 API 的大量请求中轮换 IP。在本文中——只有实践:命令、配置、代码示例,没有废话。

1. curl 和 wget 如何与代理工作:基本机制

在深入配置之前,重要的是要理解底层发生了什么。这两个工具支持两种主要的代理协议:HTTP/HTTPS 和 SOCKS5。它们的机制不同,这影响了在特定任务中选择哪种类型的代理。

HTTP 代理 作为应用层协议的中介工作。当 curl 通过 HTTP 代理发送请求时,它实际上是在告诉代理服务器:“替我发起一个 GET 请求到这个 URL。”对于 HTTPS 流量,使用 CONNECT 方法——curl 请求代理建立到目标主机的隧道,然后 TLS 握手直接在客户端和目标服务器之间进行。这一点很重要:在这种情况下,代理无法看到 HTTPS 流量的内容。

SOCKS5 在更低的层次上工作——它代理 TCP/UDP 连接,而不依赖于应用层协议。这使得 SOCKS5 更加通用:它不仅可以转发 HTTP/HTTPS,还可以处理其他协议。此外,SOCKS5 支持在代理服务器端进行 DNS 解析——这对于防止 DNS 泄漏至关重要。

💡 实践中的关键区别:

如果只是需要下载文件或进行 API 请求——HTTP 代理就足够了。如果需要完全匿名、绕过 DNS 泄漏或处理非标准协议——请使用 SOCKS5。

curl 从非常早期的版本开始就原生支持这两种协议。wget 历史上对 SOCKS5 的支持较为有限——在旧版本中(1.19 之前)根本没有 SOCKS5 的支持,只有 HTTP 代理。在编写需要在不同发行版上运行的脚本时,这一点需要考虑。

可以使用命令 wget --version 检查 wget 的版本。对于 curl,可以使用 curl --version,在那里也可以看到 libcurl 库是用哪些协议构建的。

2. 环境变量:最快的设置方法

为 curl 和 wget 设置代理的最优雅方法是通过环境变量。这是系统级的:不需要更改每个脚本,只需在会话中设置一次变量或将其添加到用户配置文件中即可。

这两个工具读取相同的标准变量:

# 对于 HTTP 流量
export http_proxy="http://proxy.example.com:3128"

# 对于 HTTPS 流量
export https_proxy="http://proxy.example.com:3128"

# 在大写中重复(某些程序仅读取它们)
export HTTP_PROXY="http://proxy.example.com:3128"
export HTTPS_PROXY="http://proxy.example.com:3128"

# 对于 FTP(如果需要)
export ftp_proxy="http://proxy.example.com:3128"

# 排除项——不通过代理的地址
export no_proxy="localhost,127.0.0.1,::1,192.168.0.0/16,.internal.company.com"

重要的细节:curl 对变量的大小写敏感,具体取决于版本。为了避免意外——始终设置两个版本:小写和大写。这是 DevOps 中的标准实践。

要使设置对特定用户持续有效,请将行添加到 ~/.bashrc~/.profile。对于系统脚本和服务——在 /etc/environment 中或通过 Environment= 指令在 systemd 单元文件中。

# /etc/systemd/system/myservice.service
[Service]
Environment="http_proxy=http://proxy.example.com:3128"
Environment="https_proxy=http://proxy.example.com:3128"
Environment="no_proxy=localhost,127.0.0.1"
ExecStart=/usr/bin/myapp

如果代理需要身份验证,用户名和密码直接插入变量的 URL 中:

export http_proxy="http://username:[email protected]:3128"
export https_proxy="http://username:[email protected]:3128"

⚠️ 安全性:

不要在生产服务器的 .bashrc 或环境变量中以明文形式存储密码。使用保险库解决方案(HashiCorp Vault、AWS Secrets Manager)或至少限制对包含变量的文件的访问权限。

3. curl 的代理标志:完整解析

curl 提供了一组丰富的标志,用于直接从命令行管理代理。这在需要通过代理进行一次性请求而不更改系统设置时非常方便。

主要标志是 -x--proxy

# 基本 HTTP 代理
curl -x http://proxy.example.com:3128 https://api.example.com/data

# 简短形式
curl -x proxy.example.com:3128 https://api.example.com/data

# 显式指定协议
curl --proxy http://proxy.example.com:3128 https://api.example.com/data

# 通过代理检查外部 IP
curl -x http://proxy.example.com:3128 https://api.ipify.org

要忽略环境变量并直接连接(绕过已配置的代理),使用标志 --noproxy

# 忽略特定主机的代理
curl --noproxy "internal.company.com" https://internal.company.com/api

# 完全忽略代理(即使设置了环境变量)
curl --noproxy "*" https://api.example.com/data

用于调试代理连接的有用标志:

# 详细模式:可以看到所有头信息,包括与代理的 CONNECT
curl -v -x http://proxy.example.com:3128 https://api.example.com/data

# 仅响应头
curl -I -x http://proxy.example.com:3128 https://api.example.com/data

# 显示通过代理的连接时间
curl -w "Connect: %{time_connect}s\nTotal: %{time_total}s\n" \
  -x http://proxy.example.com:3128 \
  -o /dev/null -s https://api.example.com/data

通过配置文件 ~/.curlrc 设置代理——如果不想每次都输入标志,这很方便:

# ~/.curlrc
proxy = http://proxy.example.com:3128
proxy-user = username:password
noproxy = localhost,127.0.0.1,.internal.company.com

对于系统脚本,可以创建单独的配置,并通过 curl -K /path/to/config 显式指定——这允许为不同任务拥有不同的代理配置文件。

4. 在 wget 中设置代理:标志和配置文件

wget 的灵活性不如 curl,但对于典型任务——下载文件、递归镜像网站——它的功能完全足够。可以通过三种方式在 wget 中设置代理:通过环境变量(上面讨论过)、通过命令行标志和通过配置文件 ~/.wgetrc

wget 的命令行标志用于代理:

# 通过标志设置 HTTP 代理
wget -e use_proxy=yes \
     -e http_proxy=http://proxy.example.com:3128 \
     https://example.com/file.tar.gz

# 通过代理设置 HTTPS
wget -e use_proxy=yes \
     -e https_proxy=http://proxy.example.com:3128 \
     https://example.com/file.tar.gz

# 带身份验证
wget -e use_proxy=yes \
     -e http_proxy=http://username:[email protected]:3128 \
     https://example.com/file.tar.gz

# 对于特定命令禁用代理(如果设置了 env 变量)
wget --no-proxy https://internal.company.com/file.tar.gz

配置文件 ~/.wgetrc 是持续设置的首选方式:

# ~/.wgetrc
use_proxy = on
http_proxy = http://proxy.example.com:3128
https_proxy = http://proxy.example.com:3128
ftp_proxy = http://proxy.example.com:3128
no_proxy = localhost,127.0.0.1,.internal.company.com

# 如果代理需要身份验证
proxy_user = username
proxy_password = secretpassword

对于系统应用(所有用户),使用 /etc/wgetrc——格式相同,但全局应用。适用于所有下载操作必须通过企业代理的服务器。

实用示例:通过代理递归下载网站,限制深度和速度:

wget -e use_proxy=yes \
     -e http_proxy=http://proxy.example.com:3128 \
     --recursive \
     --level=2 \
     --limit-rate=500k \
     --wait=1 \
     --random-wait \
     --user-agent="Mozilla/5.0 (compatible; Googlebot/2.1)" \
     https://example.com/docs/

5. curl 和 wget 中的 SOCKS5 代理:设置和示例

SOCKS5 是处理匿名性或使用非标准端口任务的更优选协议。对于系统管理员和 DevOps 工程师,SOCKS5 通常在通过 SSH 隧道工作时使用,以及在连接到 住宅代理 时,这些代理模拟真实用户的流量。

在 curl 中,SOCKS5 通过代理 URL 中的特殊前缀支持:

# SOCKS5 在客户端进行 DNS 解析(可能会有 DNS 泄漏!)
curl --socks5 proxy.example.com:1080 https://api.example.com/data

# SOCKS5 在代理端进行 DNS 解析(推荐!)
curl --socks5-hostname proxy.example.com:1080 https://api.example.com/data

# 通过 -x 标志显式指定协议
curl -x socks5h://proxy.example.com:1080 https://api.example.com/data

# 带身份验证
curl -x socks5h://username:[email protected]:1080 https://api.example.com/data

# 通过环境变量使用 SOCKS5
export all_proxy="socks5h://proxy.example.com:1080"
curl https://api.example.com/data

📌 socks5 与 socks5h——有什么区别?

socks5——DNS 查询在本地执行,通过代理仅进行 TCP 连接。可能会有 DNS 泄漏。
socks5h(h = 主机名)——DNS 查询在代理服务器端执行。完全匿名。推荐用于大多数任务。

DevOps 中的一个流行场景是使用 SSH 作为 SOCKS5 代理,通过堡垒主机隧道流量:

# 在本地端口 1080 上打开带 SOCKS5 的 SSH 隧道
ssh -D 1080 -f -C -q -N [email protected]

# 现在在 curl 中使用这个隧道
curl -x socks5h://localhost:1080 https://internal-api.private.network/data

# 或通过环境变量为会话中的所有请求使用
export all_proxy="socks5h://localhost:1080"
wget https://internal-resource.private.network/file.tar.gz

wget 从版本 1.19 开始支持 SOCKS5。在旧版本(CentOS 7、Ubuntu 16.04)中,您需要使用替代解决方案:proxychains、tsocks,或者在需要 SOCKS5 的任务中切换到 curl。

6. 代理身份验证:用户名和密码

大多数商业代理和企业代理服务器都需要身份验证。有几种方法可以传递凭据——每种方法在安全性方面都有其优缺点。

方法 1:在 URL 中提供凭据——简单,但不安全(密码在进程列表中可见):

curl -x http://user:p%[email protected]:3128 https://api.example.com/data
# 密码中的特殊字符需要进行 URL 编码:@ → %40, : → %3A

方法 2:curl 中的 --proxy-user 标志——可以不在命令行中指定密码,curl 会交互式地请求它:

# 在标志中提供用户名和密码(仍然在 ps aux 中可见)
curl -x http://proxy.example.com:3128 \
     --proxy-user "username:password" \
     https://api.example.com/data

# 仅提供用户名——curl 会交互式地询问密码
curl -x http://proxy.example.com:3128 \
     --proxy-user "username" \
     https://api.example.com/data

方法 3:通过 .netrc 文件——对于脚本来说是最安全的:

# ~/.netrc
machine proxy.example.com
login username
password secretpassword

# 限制对文件的访问权限
chmod 600 ~/.netrc

# 在 curl 中使用
curl -x http://proxy.example.com:3128 --proxy-netrc https://api.example.com/data

方法 4:通过秘密存储中的环境变量——推荐用于 CI/CD 和生产环境:

# 在脚本中从变量中读取凭据(由 CI/CD 设置)
#!/bin/bash
PROXY_URL="http://${PROXY_USER}:${PROXY_PASS}@proxy.example.com:3128"
curl -x "${PROXY_URL}" https://api.example.com/data

身份验证方法的比较表:

方法 便利性 安全性 适合于
URL (user:pass@host) ⭐⭐⭐ 测试
--proxy-user 标志 ⭐⭐⭐ ⭐⭐ 一次性命令
.netrc 文件 ⭐⭐ ⭐⭐⭐ 本地脚本
来自保险库的环境变量 ⭐⭐ ⭐⭐⭐⭐⭐ CI/CD,生产

7. 排除和 no_proxy:如何绕过本地地址的代理

在企业和云环境中,通常需要精细的设置:外部流量——通过代理,内部流量——直接。变量 no_proxy(或 NO_PROXY)允许指定排除列表。

# 基本排除项
export no_proxy="localhost,127.0.0.1,::1"

# 按域名排除(带点——所有子域)
export no_proxy="localhost,127.0.0.1,.internal.company.com,.corp.local"

# 按 IP 范围排除(CIDR 表示法并非在所有地方都有效!)
export no_proxy="localhost,127.0.0.1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16"

# 对于 AWS:排除元数据端点和内部地址
export no_proxy="localhost,127.0.0.1,169.254.169.254,.amazonaws.com.internal"

⚠️ no_proxy 的重要特性:

CIDR 表示法(10.0.0.0/8)并非所有工具都支持。curl 从版本 7.86.0 开始支持。wget——根本不支持。为了兼容性,最好列出具体的 IP 或掩码,如 10.(所有以 10. 开头的)。

针对 Kubernetes 环境的实用示例,需要排除 API 服务器和内部服务:

export no_proxy="localhost,127.0.0.1,10.96.0.0/12,10.244.0.0/16,.cluster.local,.svc,.default"
export NO_PROXY="${no_proxy}"

8. CI/CD 中的代理:GitHub Actions、GitLab CI、Docker

在 CI/CD 管道中设置代理是 DevOps 工程师在企业网络或有限互联网访问情况下最常见的任务之一。我们将讨论流行平台的具体配置。

GitHub Actions

# .github/workflows/deploy.yml
name: Deploy

on: [push]

jobs:
  deploy:
    runs-on: ubuntu-latest
    env:
      http_proxy: ${{ secrets.PROXY_URL }}
      https_proxy: ${{ secrets.PROXY_URL }}
      no_proxy: "localhost,127.0.0.1,.github.com"
    
    steps:
      - uses: actions/checkout@v3
      
      - name: 下载依赖
        run: |
          curl -x "${http_proxy}" https://external-api.example.com/config.json -o config.json
          wget -e use_proxy=yes -e http_proxy="${http_proxy}" https://releases.example.com/app-v1.0.tar.gz

GitLab CI

# .gitlab-ci.yml
variables:
  http_proxy: "http://proxy.company.com:3128"
  https_proxy: "http://proxy.company.com:3128"
  no_proxy: "localhost,127.0.0.1,.gitlab.company.com"

build:
  stage: build
  script:
    - curl -x "${http_proxy}" https://registry.npmjs.org/package -o package.json
    - wget -e use_proxy=yes -e http_proxy="${http_proxy}" https://example.com/resource.tar.gz

Docker:构建镜像时的代理

# 通过构建参数传递代理
docker build \
  --build-arg http_proxy=http://proxy.example.com:3128 \
  --build-arg https_proxy=http://proxy.example.com:3128 \
  --build-arg no_proxy=localhost,127.0.0.1 \
  -t myapp:latest .

# 在 Dockerfile 中使用 ARG 获取变量
# Dockerfile
FROM ubuntu:22.04

ARG http_proxy
ARG https_proxy
ARG no_proxy

ENV http_proxy=${http_proxy}
ENV https_proxy=${https_proxy}
ENV no_proxy=${no_proxy}

RUN apt-get update && apt-get install -y curl wget

RUN curl -x "${http_proxy}" https://example.com/setup.sh | bash

# 在最终镜像中清除代理变量(可选)
ENV http_proxy=""
ENV https_proxy=""

Docker 守护进程的全局代理设置(通过代理进行 docker pull):

# /etc/systemd/system/docker.service.d/proxy.conf
[Service]
Environment="HTTP_PROXY=http://proxy.example.com:3128"
Environment="HTTPS_PROXY=http://proxy.example.com:3128"
Environment="NO_PROXY=localhost,127.0.0.1,.internal.registry.com"

# 重启 docker
systemctl daemon-reload
systemctl restart docker

9. 在 bash 脚本中轮换代理

如果您需要向外部 API 或服务发出大量请求——代理轮换可以分散负载并避免 IP 被封锁。这在价格监控、数据收集或从不同地区测试资源可用性时尤为重要。

对于这些任务,数据中心代理 非常合适——它们在大量请求时提供高速度和稳定性。

#!/bin/bash
# rotate_proxy.sh — 从列表中轮换代理

PROXY_LIST=(
  "http://user:[email protected]:3128"
  "http://user:[email protected]:3128"
  "http://user:[email protected]:3128"
  "http://user:[email protected]:3128"
)

URLS_FILE="urls.txt"
OUTPUT_DIR="./results"
mkdir -p "${OUTPUT_DIR}"

PROXY_COUNT=${#PROXY_LIST[@]}
INDEX=0

while IFS= read -r url; do
  PROXY="${PROXY_LIST[$INDEX]}"
  FILENAME=$(echo "${url}" | md5sum | cut -d' ' -f1)
  
  echo "请求:${url} 通过代理 $((INDEX + 1))/${PROXY_COUNT}"
  
  curl -x "${PROXY}" \
       --max-time 30 \
       --retry 3 \
       --retry-delay 2 \
       --silent \
       --output "${OUTPUT_DIR}/${FILENAME}.html" \
       "${url}"
  
  if [ $? -eq 0 ]; then
    echo "  ✓ 成功"
  else
    echo "  ✗ 失败,尝试下一个代理..."
    INDEX=$(( (INDEX + 1) % PROXY_COUNT ))
    curl -x "${PROXY_LIST[$INDEX]}" \
         --max-time 30 \
         --silent \
         --output "${OUTPUT_DIR}/${FILENAME}.html" \
         "${url}"
  fi
  
  # 转到下一个代理
  INDEX=$(( (INDEX + 1) % PROXY_COUNT ))
  
  # 请求之间稍作暂停
  sleep 0.5
  
done < "${URLS_FILE}"

echo "完成!结果已保存到 ${OUTPUT_DIR}"

更高级的选项——在使用之前检查代理的可用性:

#!/bin/bash
# check_proxy.sh — 检查代理的可用性

check_proxy() {
  local proxy_url="$1"
  local test_url="https://api.ipify.org"
  
  result=$(curl -x "${proxy_url}" \
                --max-time 10 \
                --silent \
                --write-out "%{http_code}" \
                --output /dev/null \
                "${test_url}")
  
  if [ "${result}" -eq 200 ]; then
    echo "存活"
  else
    echo "死亡"
  fi
}

# 通过代理获取外部 IP
get_proxy_ip() {
  local proxy_url="$1"
  curl -x "${proxy_url}" --max-time 10 --silent https://api.ipify.org
}

PROXY="http://user:[email protected]:3128"
STATUS=$(check_proxy "${PROXY}")
echo "代理状态:${STATUS}"

if [ "${STATUS}" == "存活" ]; then
  EXTERNAL_IP=$(get_proxy_ip "${PROXY}")
  echo "通过代理获取的外部 IP:${EXTERNAL_IP}"
fi

10. 故障排除和常见错误

使用代理不可避免地会遇到错误——尤其是在初始设置时。我们将讨论最常见的问题及其诊断方法。

通过详细模式进行诊断

# curl 的详细输出
curl -vvv -x http://proxy.example.com:3128 https://api.example.com/data 2>&1 | head -50

# 查看输出中的内容:
# * 连接到 proxy.example.com — 与代理的连接已建立
# CONNECT api.example.com:443 — 请求隧道
# HTTP/1.1 200 连接已建立 — 隧道已打开
# * SSL 连接使用 TLS — TLS 正在工作

# 调试 wget
wget -d -e use_proxy=yes -e http_proxy=http://proxy.example.com:3128 https://api.example.com/data

常见错误及解决方案

错误 原因 解决方案
407 代理身份验证失败 未提供凭据 在代理 URL 中添加 user:pass 或使用 --proxy-user 标志
连接被拒绝 端口错误或代理不可用 检查端口:nc -zv proxy.host 3128
SSL 证书错误 企业代理带有 SSL 检查 添加企业 CA:--cacert /path/to/ca.crt
无法解析代理 DNS 无法解析代理名称 使用 IP 而不是名称或检查 DNS
超时 代理缓慢或过载 增加超时:--max-time
```