V2: Rewrite GET as POST request?

Hi, is it possible to rewrite a request from GET to POST, sort of like described here? The last comment said something about it working in Caddy 2.0.

My use-case is a GraphQL query to Hasura which currently only supports POST requests. I’d like to enable GET requests so I can cache using a CDN. Thanks!

Hi @Rob, welcome to the Caddy community!

Just to note, the forum post you linked describes Caddy’s own upstream health checking, and changing which method it uses for that.

It’s a little bit different from what I would call translating a client’s request to a different method before proxying it upstream.

Of note - Caddy 2 currently does neither, but it’s still in development, so maybe you can mention this thread (possibly also the one you linked) when you submit a v2 feature request on the Github repository at https://github.com/caddyserver/caddy/?

Ah, thanks for that clarification @Whitestrake! I’ll consider opening an issue if I can’t find another way to do what I want.

That’s not quite right :slight_smile: The rewrite handler can change the request method: https://caddyserver.com/docs/json/apps/http/servers/routes/handle/rewrite/#method

2 Likes

There I go again, basing my assertions off the Caddyfile instead of the actual capabilities under the hood!

One day I’ll learn… Thanks for the clarification.

1 Like

Can we add a subdirective to rewrite for this before the RCs? Something like:

rewrite * /foo.html {
    method POST
}

Maybe you can think of a better way.

Maybe just a new directive? method /foo.html POST

1 Like

Sounds good to me :+1:

What does this do in non-reverse-proxy situations?

:man_shrugging:
Still changes the method. For if any future handlers later in the middleware chain care about the method, I guess.

1 Like

Thanks @matt. I have one more question. As far as I can tell from the docs, it is not possible to set the body of the request. Is that correct?

In my case I want to use the query arguments from a GET request to set the body of a new POST request. Thanks!

You want the server to set/change the request body for any later handlers (like reverse proxy for example)?

Not currently implemented but easy to add to the request_body handler: https://caddyserver.com/docs/json/apps/http/servers/routes/handle/request_body/

Yes, I think so. Are you offering to make a change? If so, sounds great! Maybe I should give complete background to my motivation:

Someone made this suggestion to me as a solution to caching POST requests via CDN.

My current Caddyfile looks like:

api.revddit.com {
    proxy /q/ 172.17.0.1:9090 {
        websocket
        without /q
    }
    proxy / graphql-engine:8080 {
        websocket
    }
}

And I have a docker-compose.yaml with the following:

  caddy:
    image: abiosoft/caddy:0.11.0
    depends_on:
    - "graphql-engine"
    restart: always
    ports:
    - "80:80"
    - "443:443"
    volumes:
    - ./Caddyfile:/etc/Caddyfile
    - caddy_certs:/root/.caddy

I would need to update the above to Caddy 2. This setup comes from a Digital Ocean droplet image provided by Hasura, which is a GraphQL engine.

Should be pretty simple to do the same as what you already have in v2:

api.revddit.com {
    route /q/* {
        uri strip_prefix q/
        reverse_proxy 172.17.0.1:9090
    }
    reverse_proxy graphql-engine:8080
}

Thanks for showing what my config should look like in v2.

My goal here is to transform GET requests to POST requests by setting the body which I think requires an update within Caddy itself and additional configuration within my Caddyfile. I’m also checking with Hasura devs to see if I can use the caddy/caddy docker image instead of abiosoft/caddy.

So you would need something like:

api.revddit.com {
    route /q/* {
        method POST
        uri strip_prefix q/
        request_body {http.request.uri.query.param}
        reverse_proxy 172.17.0.1:9090
    }
    reverse_proxy graphql-engine:8080
}

But right now the method and request_body lines don’t exist. (They’re extremely easy to implement, I think. PRs welcomed.)

Hi @matt, do I need to change existing code or add a module?

I’m unfamiliar with Go and the Caddy code-base, so I would not say it’s easy for me. I can give it a try.

It’d be new Caddyfile directives for existing modules, that’s all.

You don’t have to do it yourself, but it would probably be faster, since I myself don’t plan on adding new features until after the 2.0 release.

The method directive would come from the rewrite handler, in here: https://github.com/caddyserver/caddy/blob/v2/modules/caddyhttp/rewrite/caddyfile.go

And the request_body handler does not yet have a directive: https://github.com/caddyserver/caddy/tree/v2/modules/caddyhttp/requestbody

Here’s some godoc: https://pkg.go.dev/github.com/caddyserver/caddy/v2@v2.0.0-beta.18/caddyconfig/httpcaddyfile?tab=doc#RegisterHandlerDirective

Again, don’t feel obligated to, it’s just a matter of time at this point. (Anyone could do this, as well.)

Great, thank you for these details! I’ll give it a shot after I read a bit more about docker.

I think uri is also missing as a handler for route because it shows this error when I tried to upgrade my existing config,

caddy_1 | run: adapting config using caddyfile: parsing caddyfile tokens for 'route': /etc/caddy/Caddyfile:3 - Error during parsing: unrecognized directive: uri

I used,

api.revddit.com {
    route /q/* {
        uri strip_prefix q/
        reverse_proxy 172.17.0.1:9090
    }
    reverse_proxy graphql-engine:8080
}

I’ll try to learn some Go now. The config I hope to get working is,

api.revddit.com {
    route /q/* {
        uri strip_prefix q/
        reverse_proxy 172.17.0.1:9090
    }
    route /v1/graphql {
        request_body {http.request.uri.query.param}
        method POST
    }
    reverse_proxy graphql-engine:8080
}
1 Like