Docker "http: server gave HTTP response to HTTPS client" - Insecure Registry
The Docker daemon tried to talk to the registry over HTTPS, but the registry answered with plain HTTP. The daemon assumes TLS by default, so it refuses the mismatched response.
What this error means
A docker push/pull to a local or self-hosted registry (often on :5000) fails with http: server gave HTTP response to HTTPS client. It is deterministic for that registry until the daemon is told the host serves HTTP.
Error response from daemon: Get "https://registry.local:5000/v2/":
http: server gave HTTP response to HTTPS clientDiagnose 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.
# 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 plain HTTP, not HTTPS
A development or internal registry without TLS answers over HTTP. The daemon defaults to HTTPS for every registry, so the HTTP response looks wrong.
The registry is not listed as insecure
Until the host is in insecure-registries, the daemon will not fall back to HTTP for it, so every request is attempted over TLS and fails.
How to fix it
Declare the registry insecure (HTTP) in the daemon
Add the host to insecure-registries in daemon.json, then restart Docker. Suitable for CI/dev registries on a trusted network.
# /etc/docker/daemon.json
{ "insecure-registries": ["registry.local:5000"] }
sudo systemctl restart dockerPrefer enabling TLS on the registry
- Front the registry with a certificate (a private CA or public cert) and use HTTPS.
- If you must use HTTP, restrict it to an isolated CI network.
- Keep the insecure-registries list minimal and explicit.
Authenticate in the job, not in the image
- 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: writeHow to prevent it
- Serve registries over HTTPS wherever possible.
- Scope
insecure-registriesto specific internal hosts only. - Document the insecure-registry requirement next to the CI registry config.