Fixing HTTP 404: “invalid character ‘p’ after top-level value” when running a Docker registry behind Traefik reverse proxy

For ninkik, we experimented with Traefik as a reverse proxy to restrict access to specific Docker image tags. During our tests, we stumbled upon the issue that in the local development environment, a docker push returned the error

error parsing HTTP 404 response body: invalid character 'p' after top-level value: "404 page not found\n"

Reproducing the issue in our test setup

Our local development environment had been a pretty simple setup:

  • a working registry with the official Docker registry image registry:2 without any HTTPS/certificates enabled
  • a traefik single-binary to make it easier to develop a custom plug-in for ninkik
Our test setup

When doing a simple docker push, the error occurred:

$ docker push localhost:4000/registry
Using default tag: latest
The push refers to repository [localhost:4000/registry]
065fcc6943fa: Preparing
96674a19c926: Preparing
7dd7303d54a9: Preparing
cf1e107ba39e: Preparing
e5e13b0c77cb: Preparing
error parsing HTTP 404 response body: invalid character 'p' after top-level value: "404 page not found\n"

Troubleshooting time

Google spit out the following results

but none of them fixed the issue in the first place. To make the issue easier to debug, we enabled DEBUG output as JSON in our config.toml:

[log]
    level = "DEBUG"
    filePath = "logs/traefik.log"
    format = "json"

[accessLog]
    filePath = "logs/access.log"
    format = "json"

and started the traefik binary with the option to log the request headers:

./traefik --configfile config.toml --accessLog.fields.defaultMode="keep" --accessLog.fields.headers.defaultMode="keep"

Without using any further strace debugging, the access logs pointed us to the issue:

{"ClientAddr":"127.0.0.1:41136","ClientHost":"127.0.0.1","ClientPort":"41136","ClientUsername":"-","DownstreamContentSize":19,"DownstreamStatus":404,"Duration":24536,"Overhead":24536,"RequestAddr":"localhost:4000","RequestContentSize":0,"RequestCount":1,"RequestHost":"localhost","RequestMethod":"GET","RequestPath":"/v2/","RequestPort":"4000","RequestProtocol":"HTTP/1.1","RequestScheme":"https","RetryAttempts":0,"StartLocal":"2022-12-13T11:22:38.728513505+01:00","StartUTC":"2022-12-13T10:22:38.728513505Z","TLSCipher":"TLS_AES_128_GCM_SHA256","TLSVersion":"1.3","level":"info","msg":"","time":"2022-12-13T11:22:38+01:00"}

Due to our local test environment, we had added both registries (the one in the Docker container and our traefik instance) to the insecure-registries field in /etc/docker/daemon.json. But as docker push tries HTTPS at first and ignores any certificate errors if the host is specified in insecure-registries, HTTPS is still used. That process is well documented.

traefik’s configuration had been a simple one and accepted both HTTP and HTTPS traffic on the same port:

[http]
    [http.routers]
        [http.routers.route-to-registry]
            rule="Host(`localhost`)"
            service = "registry-service"
            entryPoints = ["web"]

    [http.services]
        [http.services.registry-service.loadBalancer]
            [[http.services.registry-service.loadBalancer.servers]]
                scheme = "http"
                url = "http://localhost:5000/"

Fixing the issue

To let traefik forward the incoming connection to HTTP, the HTTPS/TLS connection must be terminated. This is done by adding the following empty configuration field to traefik’s configuration file:

[http]
    [http.routers]
        [http.routers.route-to-registry]
            rule="Host(`localhost`)"
            service = "registry-service"
            entryPoints = ["web"]

            [http.routers.route-to-registry.tls]

This is definitely not an approach to fix any situation for this issue, but it might point you in the right direction.

Please note, that it is important to set in http.services.registry-service.loadBalancer.servers the configuration parameters scheme to http and url to http://localhost:5000. Otherwise, TLS termination will not have any effect.

Categories: