Skip to content
DevOps AI ToolKit
Newsletter
All guides
Docker with AI By James Joyner IV · · 9 min read

Docker Error Guide: 'http: server gave HTTP response to HTTPS client' Registry Pull/Push Failure

Quick answer

Fix Docker's 'http: server gave HTTP response to HTTPS client' error when pulling or pushing to a private registry: configure insecure-registries in daemon.json and restart Docker.

Part of the Docker Build & Image Errors hub
  • #docker
  • #troubleshooting
  • #errors
  • #registry
Free toolkit

Fixing errors like this? Get 500 free DevOps AI prompts

500 copy-paste AI prompts for the stack you actually run — one PDF, free.

Exact Error Message

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

You see the same message on push and login as well:

$ docker push registry.internal:5000/myapp:latest
Get "https://registry.internal:5000/v2/": http: server gave HTTP response to HTTPS client

The error is symmetric: whether pulling, pushing, or logging in, Docker refuses the connection because of a protocol mismatch with the registry.

What It Means

The Docker daemon always contacts a registry over HTTPS by default. This error means the daemon reached the registry endpoint, but the registry answered with plain HTTP instead of TLS. Docker interprets an HTTP reply to its HTTPS request as a security problem and aborts rather than silently downgrading.

This almost always involves a private or self-hosted registry (a registry:2 container, Harbor in HTTP mode, or a lab registry) that is serving on HTTP without a TLS certificate. Docker will not talk plain HTTP to a registry unless you explicitly declare that endpoint trusted as an insecure registry. Public registries like Docker Hub are unaffected because they serve valid TLS.

Common Causes

  • A self-hosted registry runs on HTTP with no TLS certificate configured.
  • The registry has a certificate, but a reverse proxy or port mapping terminates it as plain HTTP.
  • The registry host:port is not listed under insecure-registries in the daemon config.
  • A typo in the registry address in daemon.json (wrong host or port) so the trust entry does not match.
  • The Docker daemon was not restarted after editing daemon.json.
  • A load balancer forwards to the registry over HTTP internally while clients expect HTTPS.

Diagnostic Commands

Confirm what the registry actually serves; a successful HTTP response and a failing HTTPS one confirm the mismatch:

curl -s http://registry.internal:5000/v2/ && echo "serves HTTP"
curl -sk https://registry.internal:5000/v2/ || echo "no HTTPS"

Check whether Docker already trusts the registry as insecure:

docker info --format '{{json .RegistryConfig.InsecureRegistryCIDRs}} {{json .RegistryConfig.IndexConfigs}}'

Inspect the current daemon configuration file:

cat /etc/docker/daemon.json

Step-by-Step Resolution

  1. Decide the correct fix. Serving a registry over plain HTTP is only appropriate for trusted internal networks or local development. For anything else, add TLS to the registry instead of trusting HTTP.

  2. If HTTP is acceptable, add the registry to insecure-registries in the daemon config. Edit /etc/docker/daemon.json (create it if absent) to include the exact host:port:

{
  "insecure-registries": ["registry.internal:5000"]
}
  1. Validate the JSON so a syntax error does not prevent the daemon from starting:
python3 -m json.tool /etc/docker/daemon.json
  1. Restart the Docker daemon so it reloads the configuration:
sudo systemctl restart docker
  1. Confirm Docker now lists the endpoint as insecure:
docker info | grep -A2 'Insecure Registries'
  1. Retry the pull or push against the same address you configured:
docker pull registry.internal:5000/myapp:latest
latest: Pulling from myapp
Digest: sha256:...
Status: Downloaded newer image for registry.internal:5000/myapp:latest
  1. For the more secure path, give the registry a real certificate and skip the insecure entry entirely. Place a trusted CA or self-signed cert at /etc/docker/certs.d/registry.internal:5000/ca.crt and Docker will use HTTPS normally.

Prevention

  • Prefer TLS on every registry. Use a certificate from your internal CA or Let’s Encrypt so no insecure-registries entry is needed and traffic is encrypted.
  • If you must use HTTP internally, restrict insecure-registries to specific host:port values, never broad CIDR ranges that would trust unintended hosts.
  • Keep the registry address in daemon.json byte-for-byte identical to what you type in image references, including the port.
  • Distribute the CA certificate to every client via /etc/docker/certs.d/<host:port>/ca.crt so self-signed registries work over HTTPS without disabling verification.
  • Roll daemon.json out through configuration management so all build and deploy hosts trust the same registries consistently.
  • Always validate daemon.json and restart Docker as part of the change; forgetting the restart is the most common reason the error persists.
  • x509: certificate signed by unknown authority — the registry serves HTTPS but with an untrusted cert; install the CA instead of using insecure mode.
  • Get "https://...": dial tcp: connection refused — the registry is unreachable, not a protocol mismatch.
  • no basic auth credentials — a login/authentication problem after the connection succeeds.
  • manifest unknown — the connection works but the requested tag does not exist.

Frequently Asked Questions

Is adding an insecure registry safe? Only on a trusted, private network. It disables TLS verification for that endpoint, so image traffic is unencrypted and unauthenticated in transit. For production or untrusted networks, configure a real certificate instead.

Why does Docker refuse HTTP automatically? The daemon defaults to HTTPS for every registry and treats an HTTP response to its HTTPS request as a potential downgrade attack. It fails closed rather than silently transmitting images over plaintext.

I edited daemon.json but the error persists. The daemon only reads the config at startup. Restart Docker with sudo systemctl restart docker, and confirm the address in insecure-registries exactly matches the host:port in your image reference, port included.

How do I fix this without disabling TLS? Give the registry a certificate and place the CA at /etc/docker/certs.d/<host:port>/ca.crt on each client. Docker then connects over HTTPS normally, with no insecure-registries entry required.

Where can I find more registry troubleshooting help? Generate daemon.json and registry setup commands for your environment with the DevOps AI prompt library, and browse more Docker guides.

Free download · 368-page PDF

Get 500 Battle-Tested DevOps AI Prompts — Free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.