The fetch path that saves builds
You run go build and the terminal hangs. The progress bar crawls for thirty seconds, then errors out because the network cannot reach the repository hosting a dependency. Or your company's firewall blocks proxy.golang.org, and the build system refuses to work. You are stuck. The Go toolchain needs to fetch modules, but the network path is broken or restricted. GOPROXY is the environment variable that controls where Go looks for modules. It defines the fetch strategy, the fallback chain, and the security boundaries for your dependencies.
The module proxy concept
Think of the module proxy as a specialized library that caches every Go module ever published. Instead of your computer calling GitHub, GitLab, or Bitbucket for every single dependency, it asks the proxy. The proxy has already downloaded the module, verified the checksums, and stored it. Your machine gets a fast, reliable download from a server optimized for this exact job.
The proxy also solves the "typosquatting" problem. If a malicious actor registers a package name that looks like a popular library, the proxy can help mitigate the risk by serving only verified content. More importantly, the proxy works with a checksum database to ensure the code you download matches the code the community expects. If the proxy does not have a module, or if you configure it that way, Go falls back to talking to the source repository directly.
GOPROXY controls the fetch path. Set it right, and builds just work.
Default behavior and the comma list
The Go toolchain checks the GOPROXY environment variable to build its fetch strategy. The value is a comma-separated list of servers. Go tries the first entry. If that server returns a 404, a timeout, or an error, Go moves to the next entry. The list continues until a server succeeds or the list is exhausted.
Here's the default configuration in action.
# Check the current proxy configuration
# The toolchain reads this to decide where to fetch modules
go env GOPROXY
# output: https://proxy.golang.org,direct
The default value https://proxy.golang.org,direct tells Go to try the public proxy first. If the proxy fails, Go falls back to direct. The keyword direct is not a URL. It instructs Go to fetch the module from the VCS repository using the module path. This fallback ensures that even if the proxy is down, you can still build your project as long as you have network access to the source repositories.
You can change this list to use a private proxy or disable the public proxy entirely. Setting GOPROXY=off disables the proxy and forces direct access. This is rare and usually only needed in air-gapped environments.
The checksum safety net
The proxy does more than serve files. It works with a checksum database to protect your supply chain. When you download a module, Go records the hash in your go.sum file. For public modules, Go also queries sum.golang.org, a public database of verified checksums. If the hash from the proxy does not match the database, the build stops.
This mechanism prevents attacks where a repository gets compromised or a proxy is hijacked. If the code changes without a version bump, the checksum mismatch triggers an error. The compiler rejects the build with verify checksum: checksum mismatch. This error is a feature. It means the security system caught a problem.
You can bypass checksum verification with the GONOSUMCHECK environment variable, but only for private modules. Using GONOSUMCHECK on public code defeats the security model. The convention is to keep verification enabled for everything except internal repositories that are not in the checksum database.
Checksums are the trust anchor. Never disable them for public code.
Private modules and internal patterns
Real projects often mix public libraries with internal code. You need the proxy for public dependencies but direct access for private repositories that live behind a firewall. The GOPRIVATE environment variable handles this. It defines a list of patterns for modules that are private. Go treats any module matching a GOPRIVATE pattern as internal. It skips the proxy lookup and skips the checksum database query for these modules.
Here's how to configure a project with private dependencies.
# Set proxy to public service with direct fallback
# This ensures public dependencies are cached and verified
export GOPROXY=https://proxy.golang.org,direct
# Define private patterns to bypass the proxy
# Go treats these paths as internal and fetches directly via VCS
export GOPRIVATE=*.internal.example.com,example.com/private
# Fetch a private module
# Go skips the proxy and clones from the internal Git server
go get example.com/private/auth@v1.2.3
The GOPRIVATE variable supports wildcards. If you set GOPRIVATE=example.com, Go treats example.com/pkg and example.com/pkg/sub as private. This pattern matching applies to the proxy lookup and the checksum database. You can also set GONOSUMCHECK to the same patterns to skip go.sum verification for private modules. This is necessary because private modules are not in the public checksum database, and Go would otherwise fail the verification step.
Private modules need private patterns. Configure GOPRIVATE or the proxy will block your internal code.
CI/CD and reproducibility
Continuous integration pipelines often need explicit proxy configuration to ensure reproducibility. Developers might have different local settings, but the CI environment should be consistent. Setting GOPROXY, GOPRIVATE, and GONOSUMCHECK in the CI configuration guarantees that every build uses the same fetch strategy.
Here's a GitHub Actions workflow snippet showing the configuration.
# Configure module fetching for the CI environment
# The proxy speeds up downloads and caches dependencies
env:
GOPROXY: "https://proxy.golang.org,direct"
# Mark corporate domains as private
# Go skips the proxy for these paths and fetches directly
GOPRIVATE: "*.corp.example.com"
# Disable checksum verification for private modules
# Private repos are not in the public checksum database
GONOSUMCHECK: "*.corp.example.com"
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.22'
# Build the project
# Go resolves dependencies using the configured proxy
- run: go build ./...
The env block sets the variables for all steps. The GOPRIVATE pattern ensures internal modules are fetched directly. The GONOSUMCHECK pattern prevents checksum errors for private code. This setup works for most organizations. If your company runs a private proxy, replace the GOPROXY URL with your internal mirror.
Pitfalls and common errors
A common trap is setting GOPROXY=off in a corporate environment without configuring the internal proxy correctly. The build fails with go: module example.com/pkg@v1.0.0: not found. Another issue is checksum mismatches. If you fetch from a source that does not match the proxy's record, the compiler rejects the build with verify checksum: checksum mismatch. You can bypass this with GONOSUMCHECK, but only for trusted private modules.
Network timeouts are another problem. If the proxy is slow or unreachable, Go waits for the timeout before falling back. This makes builds feel sluggish. You can reduce the timeout by setting GONOSUMDB to skip the checksum database query, but this reduces security. The better approach is to ensure your network allows access to proxy.golang.org and sum.golang.org. If you are behind a firewall, configure the firewall to allow these domains or set up a local mirror.
The worst proxy error is the silent timeout. Configure timeouts and fallbacks.
When to use each configuration
Use the default https://proxy.golang.org,direct when you are building open-source projects or working in a standard network environment. Use a private proxy URL when your organization requires all traffic to pass through an internal mirror for security or compliance. Use GOPROXY=direct when you are behind a firewall that blocks the public proxy and you have direct access to all required repositories. Use GOPROXY=off only for isolated development environments where no network access is allowed and all modules are vendored. Use GOPRIVATE patterns when you mix public dependencies with internal modules that live on private Git servers.
Use the proxy for speed and security. Use direct for private code. Use off only when you have no choice.