[Pitch] Package Manager HTTP Proxy Configuration

[Pitch] Package Manager HTTP Proxy Configuration

Problem

Swift Package Manager uses Foundation's URLSession for downloading binary artifacts, fetching from package registries, downloading package collections, performing OCSP checks, and installing Swift SDKs. None of these operations respect HTTP proxy configuration — not environment variables, not system settings on Linux. This means developers behind corporate firewalls can swift package resolve source dependencies (which shell out to git) but cannot download a binary target from the same server.

The situation is worse in Xcode. Because Xcode launches from launchd with a minimal environment, there is no way to pass http_proxy to SPM operations initiated from the IDE. On macOS, the system proxy may be picked up implicitly by URLSession, but this behavior is undocumented, not portable, and provides no per-project override capability.

This affects everyone behind a corporate proxy, in government/university networks, or running CI behind a firewall — essentially anyone whose binary targets, registries, or collections are accessible only through a proxy.

Proposed Solution

We introduce a file-based proxy.json configuration, stored in SPM's existing configuration directories (~/.swiftpm/configuration/proxy.json for user-level, <project>/.swiftpm/configuration/proxy.json for project-level). New CLI commands (swift package config set-proxy, get-proxy, unset-proxy) manage the configuration. When present, proxy settings are applied to the URLSessionConfiguration used by SPM's HTTP client. When absent, macOS system proxy continues to work as a fallback — no behavior change for users who don't need this.

The config file approach works uniformly across CLI, Xcode, and CI regardless of environment variable availability.

Links

7 Likes

The Ecosystem Steering Group discussed this pitch today. We agree with the motivation and have seen this problem reported in CI infrastructure across the ecosystem; however, we would like to see the proposed solution to cover both a new configuration option and an environment variable based setting. We believe that for CI systems in particular the environment variable based solution is the more natural one.

1 Like

Thanks @FranzBusch and the ESG for reviewing this! I've updated both the proposal and implementation to support environment variables as a fallback:

  • http_proxy / HTTP_PROXY — proxy URL for HTTP requests
  • https_proxy / HTTPS_PROXY — proxy URL for HTTPS requests
  • no_proxy / NO_PROXY — comma-separated bypass list

Priority order: proxy.json > environment variables > macOS system proxy. Lowercase variants take precedence (consistent with curl). The config file remains the primary mechanism for Xcode/GUI workflows where env vars aren't available, while env vars provide the natural CI experience.

Updated proposal: [Proposal] Package Manager HTTP Proxy Configuration by raiyyan089 · Pull Request #3346 · swiftlang/swift-evolution · GitHub
Implementation: Add HTTP proxy configuration support by raiyyan089 · Pull Request #10274 · swiftlang/swift-package-manager · GitHub

1 Like

Updated the priority order: environment variables now take highest precedence, followed by proxy.json, then system proxy. This is consistent with standard CLI tool conventions, a caller setting http_proxy expects it to take effect unconditionally regardless of other config sources. Proposal and implementation PRs are both updated.

1 Like

I think this would be a nice addition! And I agree with the change to have env variables take highest precedence.

Some thoughts:

  1. I would suggest adding a --global flag to the CLI to edit/view the global (~/.swiftpm/configuration/proxy.json) file, similar to this proposal: SE-0535: Add CLI for editing global mirrors configuration - #4 by 0xTim.

  2. The proposal mentions the registry configuration, and has this a "alternative considered" location for this configuration. I think maybe the mirror configuration would be more reasonable (though I think reasoning for not choosing it carries over from what you have currently).

  3. I think also that the motivation should mention the mirror configuration somehow, since that is one way to solve the problem currently. With that, you can already re-map git and binary URLs to internal domains. To be clear, I don't disagree with the proposal to add this feature, but I think it should relate it to the mirror configuration, and why that might not be a good alternative in some cases.

  4. Speaking of the mirror configuration, I think it would be nice if the proposal mentioned this, and how the proxy and mirror configuration would interact. If I set both, how will the resolve which URL to go to? Will it first "translate" the original URL via the mirror configuration, and then check the proxy configuration for that URL?

1 Like

Hi @samuelmurray, thanks for taking a look. I've updated both the proposal and implementation to address each of your points:

  • --global flag: Added to set-proxy, get-proxy, and unset-proxy, following the same pattern from SE-0535. When passed, it targets ~/.swiftpm/configuration/proxy.json directly and can be run from any directory without a Package.swift. Without the flag, commands default to project-level configuration.

  • Mirrors in motivation: Added a section explaining why mirrors don't serve as a general proxy solution, they require 1:1 URL mappings per dependency and are a URL-rewriting mechanism, not a network-routing one. Organizations with blanket "all external traffic goes through a proxy" policies need transport-level support that mirrors can't provide.

  • Proxy + mirror interaction: Documented in a new "Interaction with dependency mirrors" subsection under Detailed Design. The short version: mirrors translate the URL first (at dependency resolution time via mirrors.effective(for:)), then proxy routing, including noProxy matching, applies to the post-mirror URL. So if github.com/Org/Lib is mirrored to internal.corp/Org/Lib, your noProxy list should reference internal.corp. This is the natural layering, the transport layer sees only the final destination after rewriting.

  • Also added "Extend mirrors.json with proxy settings" to Alternatives Considered, explaining why the two concepts don't belong in the same configuration file (mirrors rewrite where a request goes; proxies control how it gets there).

Updated proposal and implementation! Thank you for the feedback.

1 Like