Caddy as a library - dynamic proxying?

Hello all. New in the forum, though I have a little bit of experience with Caddy.

If using Caddy as a library in an app, could the proxying targets be intercepted and handled by the app? I’d like to be able to target different backends depending on status I’d control in the app.
I’ve only briefly looked through the “Caddy as a library” info I can find and I see it depends on a Caddyfile, so I’m thinking no?

Thanks for any assistance you can offer.

Hi @olliephillips!

Caddy isn’t anything like a HTTP request router you might commonly find in a Go app. It doesn’t really even behave similarly internally, I believe. So, just spit-balling here, but I see two ways about it depending on your requirements.

If you can determine in advance where the proxy needs to go (i.e. from app state or some external check), you could generate your Caddyfile string in your application and feed it to the Caddy instance each time the requirements change (should be a graceful reload).

If you can’t (i.e. you need to determine the destination from some aspect of the request itself), it’s not going to be pretty, but you could start Caddy and then start up your own localhost listener, proxying everything to yourself, inspecting the request, and proxying it onwards.

The second is a bit of a poor option because your app then needs to talk HTTP(S) anyway, so there’s little difference between this and having your own app without Caddy and just putting Caddy in front separately.

1 Like

The first option looks promising. I should have considered generating the caddyfile passed. Second option not possible since I’m looking to use the automatic TLS feature of caddy. Thanks very much for the steer.

Second option won’t stop you from using Automatic HTTPS. Your app will still run Caddy at the edge listening to external clients, but it will be using Caddy to proxy to itself on some other port locally so you can do something with that forwarded request.

But yeah, it’s not ideal. Let us know how you go with option 1.

Thanks again.

I seem to be missing something basic. How do we load a caddyfile? My idea was to have my app maintain/write the caddyfile rather than build it and inject it dynamically as a string.

I can do it using caddy.CaddyfileFromPipe() but see this is not recommended in a thread I’ve found. But caddy.LoadCaddyfile("http") seems to ignore the caddyfile in same directory as my app and use the default (app starts port 2015)? I’ve also tried to pass -config "path to my caddyfile" but still it starts on port 2015?

There’s a basic overview of using Caddy programmatically at caddy package - github.com/mholt/caddy - pkg.go.dev - note that the flag is -conf not -config. See if that helps…

Thanks Matt, yes I had seen that - and the wiki example. But I’m still stuck. And i meant `-conf`` that’s what I’ve been using. The error was in my post above.

I’ve posted my code below

package main

import (
	"log"

	"github.com/mholt/caddy"
	_ "github.com/mholt/caddy/caddyhttp"
	_ "github.com/mholt/caddy/caddyhttp/httpserver"
)

func main() {
	caddy.AppName = "Test"
	caddy.AppVersion = "0.9.0"

	caddyfile, err := caddy.LoadCaddyfile("http")
	if err != nil {
		log.Fatal(err)
	}

	instance, err := caddy.Start(caddyfile)
	if err != nil {
		log.Fatal(err)
	}

	instance.Wait()
}

The caddyfile is in the same directory. The contents are:

localhost:1256 {
    root /
}

I have used this to run the app:

go run app.go -conf caddyfile

and this

go run app.go -conf /Users/olliephillips/..../Go/src/github.com/olliephillips/test/caddyfile

and this

go run app.go

All result in server listening on default port of 2015. My caddyfile is ignored. Maybe I have not grasped something fundamental.

First thing I noticed, you need only import "github.com/mholt/caddy/caddyhttp" - no need to import "github.com/mholt/caddy/caddyhttp/httpserver" as that is imported for you by the first package.

Ah, I should probably add this to the docs, Caddy doesn’t plug in any Caddyfile loaders by default. We can look into changing that, although I prefer the programmer have complete control over loading the Caddyfile. Short of it is: load it yourself, or register a Caddyfile loader using caddy.RegisterCaddyfileLoader() or caddy.SetDefaultCaddyfileLoader() - you can see how the caddy command does this in run.go.

1 Like

Great, thank you for the direction - I’ll check out run.go. Thanks again.

Edit: That did it!

Just an FYI. I added an example that loads the Caddyfile from same directory to the Wiki on “embedded use”. It also includes the imports. From the forum posts, I could see both these points were stumbling blocks for others, as indeed they were for me :slight_smile:

1 Like

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