Skip to content
Latchkey

Insecure registry "http: server gave HTTP response to HTTPS client" in CI

The docker daemon defaults to HTTPS for registries, but this registry answered over plain HTTP. The handshake fails with "server gave HTTP response to HTTPS client". For a registry that genuinely serves HTTP, declare it as an insecure registry in the daemon config; the better fix is to enable TLS.

What this error means

docker push or pull to a local/self-hosted registry (often :5000) fails with "http: server gave HTTP response to HTTPS client".

docker
Error response from daemon: Get "https://registry.internal:5000/v2/":
http: server gave HTTP response to HTTPS client

Diagnose it: separate auth from naming from rate limits

Registry errors look alike and have unrelated causes. Work out which of the three you have before changing credentials, because a malformed image reference produces an error that reads like an authentication failure.

Terminal
# 1. is the reference even valid? (lowercase, no spaces, valid tag)
docker image inspect "$IMAGE" 2>&1 | head -2

# 2. are you authenticated to the right registry?
cat ~/.docker/config.json | grep -o '"[^"]*\.[^"]*"' | head

# 3. are you rate limited? (Docker Hub anonymous pulls)
curl -s "https://auth.docker.io/token?service=registry.docker.io&scope=repository:ratelimit-preview/test:pull" \
  | grep -o '"token"' >/dev/null && echo "token ok"

Common causes

The registry serves HTTP, the daemon expects HTTPS

A registry started without TLS responds in HTTP, but docker speaks HTTPS first and rejects the mismatch.

The host is not in insecure-registries

Until the daemon is told the host is insecure, it will not fall back to HTTP.

How to fix it

Declare the registry insecure (non-prod)

  1. Add the host:port to insecure-registries in daemon.json.
  2. Restart docker so the daemon accepts HTTP for that host.
  3. Retry the push or pull.
/etc/docker/daemon.json
{ "insecure-registries": ["registry.internal:5000"] }

Enable TLS on the registry (preferred)

Serve the registry over HTTPS with a trusted certificate so no insecure exception is needed.

Authenticate in the job, not in the image

.github/workflows/ci.yml
- uses: docker/login-action@v3
  with:
    registry: ghcr.io
    username: ${{ github.actor }}
    password: ${{ secrets.GITHUB_TOKEN }}

# GHCR needs this on the job or the push is rejected as unauthorised
permissions:
  contents: read
  packages: write

How to prevent it

  • Run registries over TLS so HTTPS clients connect cleanly.
  • Reserve insecure-registries for local/dev only.
  • Configure insecure hosts on every runner that needs them.

Frequently asked questions

What causes Insecure registry "http: server gave HTTP response to HTTPS client" in CI?
There are 2 common causes: the registry serves http, the daemon expects https and the host is not in insecure-registries. A registry started without TLS responds in HTTP, but docker speaks HTTPS first and rejects the mismatch.
How do I fix Insecure registry "http: server gave HTTP response to HTTPS client" in CI?
There are 2 fixes depending on which cause you have: declare the registry insecure (non-prod) and enable tls on the registry (preferred). Work through them in order, since the first is the most common.
What does Insecure registry "http: server gave HTTP response to HTTPS client" in CI actually mean?
docker push or pull to a local/self-hosted registry (often :5000) fails with "http: server gave HTTP response to HTTPS client".
How do I stop Insecure registry "http: server gave HTTP response to HTTPS client" in CI happening again?
Run registries over TLS so HTTPS clients connect cleanly. The prevention section lists 3 changes that keep it from recurring.

Related guides

References

This is a registry failure, not a bug in your code. Latchkey detects, repairs, and retries it for you. Start free → 30-day trial · No credit card