How Do I Expose REST API of Reverse-Proxied App in Caddy

1. Caddy version

       2.46

2. How I run Caddy:

systemctl [enable | start | stop | restart ] caddy

a. System environment:

Debian 11.2

b. Command:

systemctl [enable | start | stop | restart ] caddy

c. Service/unit/compose file:

N/A

d. My complete Caddyfile or JSON config:

listmonk.14servers.net {
	# Set this path to your site's directory.
	#  root /opt

	# Enable the static file server.
	file_server

	# Another common task is to set up a reverse proxy:
	reverse_proxy * localhost:9000

	# Add compression
	encode zstd gzip
}

3. The problem I’m having:

I’m using Caddy as reverse proxy with ListMonk - an email marketing/newsletter program which, like Caddy, is also a single-file executable written in Go.

I’ve placed the ListMonk executable in /opt

My Caddyfile above is very simple, likely too simple:

My ListMonk instance is able to successfully send emails but is failing to detect open or clicked links.

I suspect this is because my caddy file is not set up properly to let ListMonk’s own REST-API be publicly accessed via the API below:

I’ve tried uncommenting the following line from my Caddyfile

‘’‘root /opt’’’

But caddy validate returns following error:

‘’’
root@hector:/etc/caddy# nano Caddyfile
root@hector:/etc/caddy# caddy validate
2022/01/31 14:54:34.781 INFO using adjacent Caddyfile
validate: adapting config using caddyfile: parsing caddyfile tokens for ‘root’: Caddyfile:13 - Error during parsing: Wrong argument count or unexpected line ending after ‘root’
‘’’

The above error seems curious to me, but given that the application works at all when the root /opt line is commented out entirely leads me to believe I’m missing (not understanding) something else important regarding the exposure of REST API’s for proxied apps.

4. Error messages and/or full log output:

5. What I already tried:

6. Links to relevant resources:

You probably don’t want root and file_server in your config, if you’re proxying to an upstream app. That only makes sense if the app does have static files that it doesn’t serve itself from its upstream server. But if it does, then you need to use request matchers in your config to tell Caddy for which requests to try to serve static files, and which to proxy upstream.

Remove everything inside of your site block except reverse_proxy. Does it work? Make requests with curl -v and show us an example request, if it doesn’t work. What’s in Caddy’s logs?

1 Like

I think all files created and delivered by ListMonk are created on-the-fly by the ListMonk executable and don’t exist in a separate directory as static files. If this is the case, then per your suggestion I probably don’t want or need to have file server activated.

Couple of quick questions:

Q1: Since my ListMonk control panel is also proxied, then when I’m trying to establish subscriber settings that themselves require a REST API, and the required matchers are missing or misconfigured, that would likely explain why ListMonk REST API commands to create Subscriber Tracking are not working…yes?

Q2: Are there any Caddy log files available by default WITHOUT specifying either the DEBUG or LOG directives in my Caddyfile?

My current revised Caddyfile below is now:

listmonk.14servers.net {
# Set this path to your site’s directory.
#root /opt

# Enable the static file server.
#file_server

# Another common task is to set up a reverse proxy:
reverse_proxy * localhost:9000

# Add compression
encode zstd gzip

}

This behaves the same as before, that is emails are delivered, the 1px tracking image is also delivered, whose end of body

URL address is:

div class=3D"gutter" style=3D"padding: 30px;"> <img src=3D"https:=
//listmonk.14servers.net:9000/campaign/0ee90265-099f-4363-a9e7-84a6bb80c8c0=
/c2b40353-dd60-48c4-bdac-a671141a53ac/px.png" alt=3D"" />

But after opening the email it’s not being registered by ListMonk indicating the REST API call https://listmonk.14servers.net/campaign/../../px.png is not callable.

Curiously, when I uncomment root /opt and perform…

caddy fmt --overwrite
caddy validate …now yields the following syntax error even though /opt is a valid directory containing the ListMonk executable.

root@hector:/etc/caddy# caddy fmt --overwrite
root@hector:/etc/caddy# caddy validate
2022/01/31 18:27:52.201 INFO using adjacent Caddyfile
validate: adapting config using caddyfile: parsing caddyfile tokens for ‘root’: Caddyfile:13 - Error during parsing: Wrong argument count or unexpected line ending after ‘root’

Does this mean I need something like …

root /opt/ …

root /opt/*

or some kind of list of request matchers corresponding to the ListMonk’s REST API.

I don’t understand. I don’t know how ListMonk works, so I don’t really know what this means.

Yes, Caddy writes its logs to stdout/stderr, which when running as a systemd service, are written to the journal. See this page in the docs, which has the recommended command to read your logs:

You can just remove those commented sections altogether, they don’t really provide any useful information for you to keep. The default Caddyfile is just meant as a “fast as possible” introduction, but doesn’t need to stick around.

But yeah, that’s probably fine. Your upstream server might be doing its own compression though, so encode might not actually do anything for you. But there’s not really any harm having it there either.

That looks strange… is there really path traversal dots in the URL it makes? :thinking:

Yes, because there’s a syntax error. You need to put a * in between root and /opt because otherwise the Caddyfile parser reads /opt as being the path matcher because it starts with a /, but it’s actually meant to be the first argument. So it should read root * /opt.

But again, you don’t need this at all if your upstream server is meant to handle these requests.

Maybe you should find documentation for ListMonk about how to properly serve it behind a reverse proxy. If you find docs for nginx, that would be useful as well because you can probably port some of the config they recommend over to Caddy (and probably skip a lot of what’s in the nginx config because Caddy’s defaults usually cover most things properly)

1 Like

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