Detecting HTTP Request Header Content-Length in an HTTP Match Expression

1. The problem I’m having:

I am upgrading from Caddy 2.4.1 to 2.7.4. It’s been a couple of years that we’ve been using Caddy without issue. It’s time to upgrade, but I’m running into an issue with an HTTP expression matcher around this block of code in the Caddy file (NOTE: I use the json structure, not the Caddyfile syntax)

{
  "handle": [
    {
      "error": "Payload Too Large",
      "handler": "error",
      "status_code": "413"
    }
  ],
  "match": [
    {
      "expression": "int({http.request.header.Content-Length}) > 15728640"
    }
  ]
},

When I remove the above block, the Caddy server starts normally.

2. Error messages and/or full log output:

{"level":"error","ts":1695327188.0788672,"logger":"http.matchers.expression","msg":"evaluating expression","error":"type conversion error from 'string' to 'int'"}

3. Caddy version:

I’m using 2.7.4-alpine docker container

4. How I installed and ran Caddy:

We use the docker image from Github

a. System environment:

Linux alpine on docker

b. Command:

caddy run --config /etc/caddy/caddy_config.json

c. Service/unit/compose file:

Docker container running in a Kubernetes pod

d. My complete Caddy config:

{
  "admin": {
    "config": {
      "persist": false
    },
    "enforce_origin": false,
    "listen": "0.0.0.0:2019",
    "origins": []
  },
  "apps": {
    "http": {
      "servers": {
        "srv0": {
          "errors": {
            "routes": [
              {
                "handle": [
                  {
                    "body": "Payload Too Large",
                    "close": true,
                    "handler": "static_response",
                    "status_code": "413"
                  }
                ],
                "match": [
                  {
                    "vars": {
                      "status_code": "413"
                    }
                  }
                ],
                "terminal": false
              }
            ]
          },
          "listen": [
            ":80"
          ],
          "logs": {
            "default_logger_name": "log0"
          },
          "max_header_bytes": 2097152,
          "routes": [
            {
              "handle": [
                {
                  "handler": "subroute",
                  "routes": [
                    {
                      "handle": [
                        {
                          "error": "Payload Too Large",
                          "handler": "error",
                          "status_code": "413"
                        }
                      ],
                      "match": [
                        {
                          "expression": "int({http.request.header.Content-Length}) > 15728640"
                        }
                      ]
                    }
                  ]
                },
                {
                  "handler": "subroute",
                  "routes": [
                    {
                      "handle": [
                        {
                          "handler": "rewrite",
                          "uri": "/"
                        },
                        {
                          "handler": "file_server",
                          "hide": [
                            "/etc/caddy/Caddyfile"
                          ]
                        }
                      ]
                    }
                  ]
                }
              ],
              "match": [
                {
                  "host": [
                    "localhost"
                  ]
                }
              ],
              "terminal": true
            }
          ]
        }
      }
    }
  },
  "logging": {
    "logs": {
      "default": {
        "exclude": [
          "http.log.access.log0"
        ],
        "level": "warn"
      },
      "log0": {
        "encoder": {
          "format": "json"
        },
        "include": [
          "http.log.access.log0"
        ],
        "level": "info",
        "writer": {
          "output": "stdout"
        }
      }
    }
  }
}

5. Links to relevant resources:

This post:

seems to suggest that the syntax is correct, but it is three years old and I’m not sure if it is still valid.

This should work:

{http.request.header.Content-Length} != "" && int({http.request.header.Content-Length}) > 100

The problem is that requests with no Content-Length header will have the placeholder return an empty string, and "" can’t be converted to a number (arguably it should convert to 0 but :man_shrugging: that’s my PHP brain talking).

2 Likes

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.