skip to content

What is GOPROXY, and where does the go command fetch modules from by default?

level: juniorimportance: should knowfreq 58%

answer

  1. an ordered list of sources
  2. one public default, one fallback word
  3. comma and pipe fall through differently
  4. mirror keeps copies upstream may delete

basics

~20 s

GOPROXY lists the module servers the go command downloads dependencies from. It defaults to https://proxy.golang.org,direct: try Google's public module mirror first, and if the module is not there, fetch it straight from its version-control origin.

solid answer

~40 s

GOPROXY is an ordered list of module sources. The default is `https://proxy.golang.org,direct`, so the go command asks the public mirror for a module's `.info`, `.mod` and `.zip`, and falls back to `direct` — cloning from the version-control origin the module path points at — when the mirror answers 404 or 410. The separator matters: a comma falls through only on 404/410, while a pipe (`|`) falls through on any error, including a proxy that is down. `GOPROXY=off` forbids downloads entirely, which is how you make a build hermetic. Teams use a proxy because it serves immutable cached copies, so a deleted, renamed or unreachable upstream repository no longer breaks builds; it is also faster and means build machines need no VCS credentials for public code.

code

text · 11 lines
text
# default: public mirror first, then the version-control origin on 404/410
GOPROXY=https://proxy.golang.org,direct

# internal mirror is authoritative; a mirror outage should fail the build
GOPROXY=https://mirror.corp.example.com,direct

# internal mirror, but fall through on ANY error, not just 404/410
GOPROXY=https://mirror.corp.example.com|https://proxy.golang.org,direct

# hermetic: nothing may be downloaded; missing modules are a build error
GOPROXY=off

go deeper

for a junior

Be ready to say what GOPROXY is for, quote the default value, and explain that direct means cloning from the module's own repository. Knowing that off blocks downloads entirely is a good extra.

for a middle

Explain the proxy protocol at the level of the files served — .info, .mod and .zip — and why serving a dependency's go.mod alone makes graph resolution cheap. The comma-versus-pipe fallback rule is the mechanic interviewers probe here.

for a senior

Show that you have configured this for a real fleet: which entry is authoritative, whether a mirror outage should fail builds or fall through, and how you confirm the value in effect with go env GOPROXY before debugging anything else.

for a principal

Own the argument that a module source is an availability dependency of every build in the organisation. Be able to say what you gain by putting a mirror in the path, what it costs to run, and what your fallback behaviour is when it is down.

## What GOPROXY is `GOPROXY` is an environment variable holding a comma- or pipe-separated, **ordered** list of places the `go` command may download module code from. Its default is: ``` GOPROXY=https://proxy.golang.org,direct ``` Every entry is either an HTTP(S) endpoint speaking the **module proxy protocol**, the literal word `direct`, or the literal word `off`. ## The module proxy protocol A module proxy is a very small read-only HTTP API. For a module path and version the go command requests a handful of files under `<proxy>/<module path>/@v/`: - `list` — the known versions of the module - `<version>.info` — a small JSON blob with the version and its timestamp - `<version>.mod` — that version's `go.mod` **alone** - `<version>.zip` — the module's file tree There is also `@latest`. Notice that the proxy can serve a dependency's `go.mod` without shipping its source: that is what lets the go command build the module graph cheaply, downloading full zips only for what it actually compiles. A proxy never selects versions, never rewrites import paths and never patches source. It serves bytes for a module path exactly as that module was published. ## `direct` and `off` `direct` means: skip proxies and resolve the module path yourself. The go command turns the module path into a repository URL, invokes the version-control tool (usually `git`), fetches the tagged version and builds the zip locally. That requires network access to the origin host and, for private hosts, working credentials. `off` means: do not download anything. Any module not already in the local module cache is a hard error. This is how you assert that a build is hermetic — nothing new comes off the network. ## Comma versus pipe — a real difference The two separators differ in *which failures are worth trying the next entry for*: - **comma** — fall through to the next entry only when the current one returns HTTP **404 or 410** ("I do not have that module"). Any other error — a 500, a TLS failure, a connection refused — stops the fetch. - **pipe (`|`)** — fall through on **any** error. So `https://mirror.corp.example.com,direct` says "the mirror is authoritative; if it is broken, fail loudly", while `https://mirror.corp.example.com|direct` says "if the mirror is unavailable for any reason, just go to the origin". The first is the safer default for an internal mirror you actually want to be in the path of every build; the second trades that control for availability. ## Why a proxy at all Three reasons, in the order teams usually discover them: 1. **Availability and reproducibility.** The public mirror keeps an immutable copy of every version it has ever served. If an upstream repository is deleted, renamed, force-pushed or simply unreachable, a build that goes through the mirror still works. A `direct` build depends on every one of your transitive dependencies' hosts being up right now. 2. **Speed.** A zip from a CDN-backed mirror is much cheaper than a git clone, and CI does a lot of these. 3. **Operability.** Build machines need no VCS tooling or credentials for public dependencies, and an internal mirror gives an organisation one place to cache, audit or allowlist what enters the network. ## What GOPROXY does *not* do A common misconception is that using a proxy replaces integrity checking. It does not. Whatever the source — public mirror, internal mirror or `direct` — the go command hashes what it received and compares it against `go.sum`, and consults the checksum database for modules not already recorded. Changing GOPROXY changes **where bytes come from**, never **whether they are verified**. Equally, a proxy does not choose versions. Version selection is computed locally by the go command from the requirements in `go.mod` across the module graph; the proxy is only asked for the versions that computation names. ## Setting it `GOPROXY` is an ordinary environment variable, so CI can export it per job. For a developer machine, `go env -w GOPROXY=...` persists it in the go environment file, and `go env GOPROXY` prints the value actually in effect — worth checking first when a fetch behaves unexpectedly, because a stale `go env -w` setting outlives any shell.

  • If the public mirror never had a module version, how does the go command still get it?
    The `direct` entry at the end of the default GOPROXY takes over. The go command maps the module path to a repository URL, runs the version-control tool to fetch the matching tag or commit, and builds the module zip locally. That is why the default ends in `,direct` — without it, anything the mirror has not seen would simply fail.
  • Does routing fetches through a proxy weaken integrity checking?
    No. The go command hashes whatever it downloads and compares it against `go.sum` regardless of source, and consults the checksum database for versions not already recorded. A proxy that served altered bytes would fail verification exactly like a tampered direct clone. GOPROXY decides where content comes from, never whether it is checked.
  • How do you tell which GOPROXY value a failing build actually used?
    Run `go env GOPROXY` in the same environment as the build. Values set earlier with `go env -w` live in the go environment file and survive every new shell, so a developer machine often disagrees with CI for reasons nothing in the repository explains. Print it in the CI log before the build step.

Think of it as a shopping list of suppliers: try the warehouse that stocks everything first, and only drive to the original factory if the warehouse says it has never carried the part.

saying these in an interview costs you the question

  • Thinks the proxy chooses which dependency versions the build uses
  • Believes going through a proxy skips or replaces checksum verification
  • Says GOPROXY=direct is the default
  • Assumes comma and pipe separators behave identically
  • Thinks a proxy can rewrite or patch a module's source