V2: reverse proxy to UNIX socket?

1. My Caddy version (V2 beta 14):

2. How I run Caddy:

I use the systemd service template mentioned in the documentation.
My Caddyfile looks like this:

samvanderkris.xyz, www.samvanderkris.xyz {
        reverse_proxy * unix:/run/gunicorn.sock
        root /static /home/sam/samvanderkris
        header / Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
}

a. System environment:

Ubuntu server 19.10, caddy is running as a systemd service. The reverse proxy is supposed to expose a gunicorn/Django server.

3. The problem I’m having:

I’m trying to migrate my Caddy v1 setup to v2. I used v1 in the past to serve my Django/gunicorn server. With my current setup, caddy doesn’t give an error until a user tries to visit the website, at which point it returns a HTTP 500 and gives an error in journalctl.
My old Caddyfile (which I’m trying to recreate) looked like this:

samvanderkris.xyz, www.samvanderkris.xyz {
        root /home/sam/samvanderkris
        proxy / unix:/run/gunicorn.sock {
                except /static
        }
        header / Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
}

4. Error messages and/or full log output:

Feb 21 13:27:12 arcadia caddy[3132]: 2020/02/21 12:27:12.574        ERROR        http.log.error        making dial info: upstream unix:/run/gunicorn.sock: invalid dial address unix:/run/gunicorn.sock: address run/gunicorn.sock: missing port in address        {"request": {"method": "GET", "uri": "/", "proto": "HTTP/2.0", "remote_addr": "86.91.100.131:18666", "host": "samvanderkris.xyz", "headers": {"Cache-Control": ["max-age=0"], "User-Agent": ["Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0"], "Accept-Encoding": ["gzip, deflate, br"], "Upgrade-Insecure-Requests": ["1"], "Cookie": ["csrftoken=KTW5QIlbU5RdE5BsuRiEFUEUICvp7mbP5xG8eBbEz6N4AB2Ww96naf6tI7pU2HM6"], "Te": ["trailers"], "Accept": ["text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"], "Accept-Language": ["en-US,en;q=0.5"], "Dnt": ["1"]}, "tls": {"resumed": false, "version": 772, "ciphersuite": 4865, "proto": "h2", "proto_mutual": true, "server_name": "samvanderkris.xyz"}}}

5. What I already tried:

I’ve looked through the documentation of the reverse_proxy directive, but couldn’t find anything relevant to my problem.

The syntax for unix socket addresses is different in v2.

unix//run/gunicorn.sock

See Conventions — Caddy Documentation

3 Likes

Yes! That did it, thank you!

One last noob question: I’m trying to exclude /static from the reverse proxy and serve those directly. My current Caddyfile looks like this:

samvanderkris.xyz, www.samvanderkris.xyz {
        reverse_proxy * unix//run/gunicorn.sock
        file_server /static/* {
                root /home/sam/samvanderkris
        }
        header * Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
}

But when I try to request something in /static it 404’s and the response headers show that it was actually still passed to gunicorn. But even if I comment the reverse_proxy out, it keeps 404-ing. I checked the permissions and I’m pretty sure caddy has can read the files in my static directory.

Hey @samvdkris,

This is happening because in the standard order of directives, reverse_proxy beats file_server (see: Caddyfile Directives — Caddy Documentation).

So, you need to make sure file_server is checked first. We can control the order of directive execution manually by putting those directives in a route - in fact, the Utility section of the route documentation pretty much describes our scenario exactly (with a file_server that wants to execute before a higher-ordered directive).

So something like this should work:

samvanderkris.xyz, www.samvanderkris.xyz {
  header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
  route {
    file_server /static/* {
      root /home/sam/samvanderkris
    }
    reverse_proxy unix//run/gunicorn.sock
  }
}
3 Likes

A couple other things to add, totally optional…

Since you only have one file_server, you can pull root out and set it as a regular directive

samvanderkris.xyz, www.samvanderkris.xyz {
  root * /home/sam/samvanderkris
  header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
  route {
    file_server /static/*
    reverse_proxy unix//run/gunicorn.sock
  }
}

(note that you do need to put * after root if you do this because otherwise it would think your /home path is a matcher)

Another approach instead of using route would be to use a “not” matcher to make the reverse proxy apply to any path that is not /static:

samvanderkris.xyz, www.samvanderkris.xyz {
  root * /home/sam/samvanderkris
  header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"

  @notStatic not path /static/*
  reverse_proxy @notStatic unix//run/gunicorn.sock

  file_server
}
4 Likes

Ahh I thought it might have something to do with priorities, but didn’t know about/see the route directive. Thanks a lot for the help guys, I really appreciate it!

1 Like

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