Combining file_server and reverse_rpoxy

1. My Caddy version (caddy version):

v2.0.0-beta.20 h1:oUNG1uh0UV8LWLlAVDZolFzk112++V/pxY+fF0HLmlY=

2. How I run Caddy:

More of a conceptual thing at this point but I have the need to be able to serve up static content (error pages, graphics etc) while still also performing a proxy on all other requests to a back-end tomcat server.

For example going to https://my.site.com/static/something.jpg would not get proxied but everything else would.

a. System environment:

Windows Server 2012R2

d. My complete Caddyfile or JSON config:

{
   default_sni my.webserver.local
   http_port 80
   https_port 443
   email licensemanager@site.com
}
my.webserver.local {
   tls C:\Caddy\Certificate.pem C:\Caddy\Certificate.key
}
my.site.com {
   @all_but_static {
      not {
         path_regexp ^/static
      }
   }
   @root {
      path_regexp ^/$
   }
   @static {
      path_regexp ^/static
   }
   file_server @static {
      root C:\Caddy\www
   }
   header {
      Strict-Transport-Security "max-age=31536000;"
      Server "C15-83"
      X-XSS-Protection "0"
      X-Server-Test "19"
   }
   handle_errors {
      @502 {
         expression {http.error.status_code} == '502'
      }      
      rewrite @502 https://my.site.com/static/test.html
   }
   redir @root https://my.site.com/epledge{uri}
   tls C:\Caddy\Certificate.pem C:\Caddy\Certificate.key
   reverse_proxy @all_but_static http://localhost:8001
   log {
      output file C:\Caddy\Logs\my_site_com_access.log {
         roll_size 100
         roll_keep 7
      }
   }
}

3. The problem I’m having:

I’m wanting to be able to proxy all request to the back-end tomcat server except for when the url contains /static. I’m trying to use the path_regexp matchers and it seems to work except that the static file contents are not being served up (I do get a response of 200 though).

If i modify the config file as such I am able to access the test.html file. However this wont work the way I need it to because our sites are mixed between being root URL’s or having a context defined (/something). Ideally everything but /static would need to be ran through the proxy.

{
   default_sni my.webserver.local
   http_port 80
   https_port 443
   email licensemanager@site.com
}
my.webserver.local {
   tls C:\Caddy\Certificate.pem C:\Caddy\Certificate.key
}
my.site.com {
   @all_but_static {
      not {
         path_regexp ^/static|^/test.html
      }
   }
   @root {
      path_regexp ^/$
   }
   @static {
      path_regexp ^/static
   }
   file_server {
      root C:\Caddy\www
   }
   header {
      Strict-Transport-Security "max-age=31536000;"
      Server "C15-83"
      X-XSS-Protection "0"
      X-Server-Test "20"
   }
   handle_errors {
      @502 {
         expression {http.error.status_code} == '502'
      }      
      rewrite @502 https://my.site.com/static/test.html
   }
   redir @root https://my.site.com/epledge{uri}
   tls C:\Caddy\Certificate.pem C:\Caddy\Certificate.key
   reverse_proxy @all_but_static http://localhost:8001
   log {
      output file C:\Caddy\Logs\my_site_com_access.log {
         roll_size 100
         roll_keep 7
      }
   }
}

4. Error messages and/or full log output:

No errors from access log and running debug in the config and watching the console doesn’t show any errors.

5. What I already tried:

Just about everything I can find on google, but I’m still very new to Caddy so I can be easily skipping or not understanding something.

6. Links to relevant resources:

This is a bit opinionated, but I think it’s easier to read a Caddyfile when the matchers and the handlers they’re for are grouped together. Also, I made a couple other changes - I copied your header block in handle_errors (you mentioned this was an issue in the other thread where errors would still have the Server: Caddy header), and I set the root for the entire site rather than just on file_server. That last one may or may not fix your issue.

my.site.com {

   root * C:\Caddy\www

   tls C:\Caddy\Certificate.pem C:\Caddy\Certificate.key

   log {
      output file C:\Caddy\Logs\my_site_com_access.log {
         roll_size 100
         roll_keep 7
      }
   }

   header {
      Strict-Transport-Security "max-age=31536000;"
      Server "C15-83"
      X-XSS-Protection "0"
      X-Server-Test "20"
   }

   handle_errors {
      @502 {
         expression {http.error.status_code} == '502'
      }      
      rewrite @502 https://my.site.com/static/test.html
      header {
         Strict-Transport-Security "max-age=31536000;"
         Server "C15-83"
         X-XSS-Protection "0"
         X-Server-Test "20"
      }
   }

   @root {
      path_regexp ^/$
   }
   redir @root https://my.site.com/epledge{uri}
   

   @all_but_static {
      not {
         path_regexp ^/static|^/test.html
      }
   }
   reverse_proxy @all_but_static http://localhost:8001

   file_server
}

Also, this’ll be for you to test, but you can probably just use path /static/* instead of path_regexp for your @all_but_static matcher. This’ll do a fast prefix match instead of running regexp, more efficient. You can also specify more than one path, and it will check each of them in sequence. So for example path /static/* /test.html

2 Likes

Thanks once again and I learned yet another thing. My config file originally look similar to root * C:\Caddy\www. What I just learned was that in order for /static to be usable in the URL you have to make a C:\Caddy\www\static folder… duh!

I converted over to using path /static/* in my @all_but_static matcher but if I try to hit something like /thisshouldbeproxied I just get a 404 error via blank page but what should happen is that Tomcat should respond back with a “Page not found” page vs an empty white page.

What do your logs say? Does it reach upstream at all on those requests?

This is what I am getting at the console; is there a native way to record this information into a log file?

2020/03/31 18:22:14.090 ←[35mDEBUG←[0m  http.handlers.reverse_proxy     upstream roundtrip      {"request": {"method": "GET", "uri": "/thisdontwork1", "proto": "HTTP/1.1", "remote_addr": "10.2.15.79:32466", "host": "my.site.com", "headers": {"Accept-Encoding": ["br, gzip, deflate"], "X-Forwarded-Port": ["443"], "X-Amzn-Trace-Id": ["Root=1-5e838a56-44dd3938ce6ad54801d83040"], "Accept": ["text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"], "Accept-Language": ["en-US,en;q=0.5"], "Upgrade-Insecure-Requests": ["1"], "Cache-Control": ["max-age=0"], "Cookie": ["AWSALB=cqF3IYs+weyDzzrX0+/Eg5oYi7EIVRW3wGZfn6/LsCl0RV/nFnl2qvfokS/KCuz+ef6pIVQPQVG0b/+dkmBzz7Axu8RY3dar5ZmT+QBRneBht7B5Bfs00RS8pNVs; AWSALBCORS=cqF3IYs+weyDzzrX0+/Eg5oYi7EIVRW3wGZfn6/LsCl0RV/nFnl2qvfokS/KCuz+ef6pIVQPQVG0b/+dkmBzz7Axu8RY3dar5ZmT+QBRneBht7B5Bfs00RS8pNVs"], "X-Forwarded-For": ["71.206.99.203, 10.2.15.79"], "X-Forwarded-Proto": ["https"], "User-Agent": ["Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0"], "Dnt": ["1"]}, "tls": {"resumed": false, "version": 771, "ciphersuite": 49200, "proto": "", "proto_mutual": true, "server_name": ""}}, "headers": {"Date": ["Tue, 31 Mar 2020 18:22:14 GMT"], "Content-Length": ["0"]}, "duration": 0.0117106, "status": 404}
2020/03/31 18:22:14.094 ←[31mERROR←[0m  http.log.access.log0    handled request {"request": {"method": "GET", "uri": "/thisdontwork1", "proto": "HTTP/1.1", "remote_addr": "10.2.15.79:32466", "host": "my.site.com", "headers": {"Dnt": ["1"], "X-Forwarded-For": ["71.206.99.203"], "X-Forwarded-Proto": ["https"], "User-Agent": ["Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0"], "Accept": ["text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"], "Accept-Language": ["en-US,en;q=0.5"], "Upgrade-Insecure-Requests": ["1"], "Cache-Control": ["max-age=0"], "Accept-Encoding": ["br, gzip, deflate"], "X-Forwarded-Port": ["443"], "X-Amzn-Trace-Id": ["Root=1-5e838a56-44dd3938ce6ad54801d83040"], "Cookie": ["AWSALB=cqF3IYs+weyDzzrX0+/Eg5oYi7EIVRW3wGZfn6/LsCl0RV/nFnl2qvfokS/KCuz+ef6pIVQPQVG0b/+dkmBzz7Axu8RY3dar5ZmT+QBRneBht7B5Bfs00RS8pNVs; AWSALBCORS=cqF3IYs+weyDzzrX0+/Eg5oYi7EIVRW3wGZfn6/LsCl0RV/nFnl2qvfokS/KCuz+ef6pIVQPQVG0b/+dkmBzz7Axu8RY3dar5ZmT+QBRneBht7B5Bfs00RS8pNVs"]}, "tls": {"resumed": false, "version": 771, "ciphersuite": 49200, "proto": "", "proto_mutual": true, "server_name": ""}}, "common_log": "10.2.15.79 - - [31/Mar/2020:13:22:14 -0500] \"GET /thisdontwork1 HTTP/1.1\" 404 0", "latency": 0.015614, "size": 0, "status": 404, "resp_headers": {"Server": ["C15-83"], "X-Xss-Protection": ["0"], "Strict-Transport-Security": ["max-age=31536000;"], "X-Server-Test": ["20"], "Content-Length": ["0"], "Date": ["Tue, 31 Mar 2020 18:22:14 GMT"]}}

If i do the same thing but using the actual tomcat context /pledge/thisdontwork1 I still get a 404 but I will get an actual page served that states the page cannot be found. Are there any other logs I can output to a file (Using MS Windows) that would help us out more? I did place “debug” in the global settings but not really seeing much more.

Well, it looks like it did hit your tomcat backend considering we’re seeing http.handlers.reverse_proxy upstream roundtrip.

Any logs in your tomcat backend? I can’t really help debugging that part.

Sure is, see the log directive: log (Caddyfile directive) — Caddy Documentation

The requests are actually getting to the tomcat folder and now knowing that I’ve verified that our old setup (using IIS) works exactly the same way. I’ve toyed around with the config and initially was having an issue to where my tests were failing this is what I started with.

   @all_but_context {
      not {
         path /epledge/* /static/*
      }
   }
   redir @all_but_context /epledge{uri}

However when I would go to my.site.com/epledge it would redirect to my.site.com/epledge/epledge. In order to fix it I had to change the config into:

   @all_but_context {
      not {
         path /epledge* /static*
      }
   }
   redir @all_but_context /epledge{uri}

Is this by design / the correct way of doing this?

Yep, because /epledge/* does not match /epledge, as documented here: Request matchers (Caddyfile) — Caddy Documentation

1 Like

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