Content-disposition and internal (nginx to caddy)

Hi there, I’m trying to translate a nginx configuration to a Caddyfile and there are some parts that I’m failing to find.

1. Caddy version (caddy version):

v2.0.0

2. How I run Caddy:

./caddy run

a. System environment:

Archlinux but nothing really relevant

b. Command:

./caddy run

d. My complete Caddyfile or JSON config:

django.company.com {
    log {
        output stdout
        level DEBUG
    }
    root /static/* /path/to/back
    root /media/* /path/to/back
    root * /path/to/front/dist
    
    route {
        reverse_proxy /admin* 127.0.0.1:8001/admin
        reverse_proxy /api* 127.0.0.1:8001/api
        reverse_proxy /events* 127.0.0.1:8888/events
        try_files {path} {path}/ /index.html
        file_server
    }
}

3. The problem I’m having:

What I’m trying is to add this nginx block:

    # Media
    location /_protected {
        internal;
        alias /path/to/back/media/;
        add_header Content-disposition "attachment";
    }

translated to the Caddyfile. I’ve done some searches but I’m a bit lost on which directive I should try both for “internal” and for “Content-disposition”.

4. Error messages and/or full log output:

No errors, just don’t know how to configure.

5. What I already tried:

I’ve tried with “respond” directive, but I didn’t even got a valid Caddyfile.

Could you give any hint on where to look? Thanks in advance!

That’s quite an old version! Please upgrade to v2.3.0!

I assume internal is because your app uses X-Accel-Redirect? Caddy doesn’t support that yet, unfortunately (and probably won’t as-is, but you may emulate that behaviour later, see the issue for details).

For the header, it’s just this:

header Content-Disposition "attachment"

But that’s not so useful without internal-like functionality.

This isn’t correct - Caddy doesn’t support paths in proxy addresses.

I’d write your config like this (aside from the internal thing):

django.company.com {
	log

	@proxy path /admin* /api* /events*
	handle @proxy {
		reverse_proxy 127.0.0.1:8001
	}

	handle {
		@backend path /static/* /media/*
		root @backend /path/to/back
		root * /path/to/front/dist

		try_files {path} {path}/ /index.html
		file_server
	}
}

This makes use of the handle directive to make mutually exclusive routes.

hi @francislavoie ! Thank you for detailed answer; it essentially workerd and I’m getting somewhere. :+1:

1 Like

When should handle and when should route be used? Can route use the @proxy place holder as well?

Please open a new help thread. There’s not enough context to answer your question.

For context I am the original author of this issue on Taiga and half-responsible for some miss-configurations in that Caddyfile. I want to test and see if only the header Content-Disposition "attachment" can have the desired affect (minus some intended security issues to be addressed later).

I have tried using route and it didn’t have the intended effect:

route /_protected/* {
      uri strip_prefix /_protected
      header +Content-disposition "attachment"
      file_server {
         root /path/to/taiga-back/media
      }
   }

Would using handle with header and other such directives be any different? Otherwise I’ll have to check how to add handle_response to fix that path issue.

You can replace these lines with handle_path /_protected/*, because handle_path has implicit strip_prefix logic.

For the rest, I don’t know what you mean by “didn’t have the intended effect”. We need specifics. What do you see when you make a request with curl -v?

Please ask this in a new thread, as the issue from the original poster is already solved.

Ok, I’ve tried the configuration, and the header part is set correctly, but the issue is still only partially solved. There is still the issue on how to emulate the internal of Nginx, currently the redirect does not work and the file can be freely retrieved from GET. The relevant console log is in this original github issue.

Here is the relevant Nginx configuration taken from the official documentation:

    location /_protected {
        internal;
        alias /path/to/back/media/;
        add_header Content-disposition "attachment";
    }
    location /media {
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Scheme $scheme;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass http://127.0.0.1:8003/;
        proxy_redirect off;
    }

And the current Caddyfile section:

   handle_path /_protected/* {
      header +Content-disposition "attachment"
      file_server {
         root /path/to/back/media
      }
   }
   handle_path /media/* {
      reverse_proxy localhost:8003
   }

I believe this is only an issue with X-Accel-Redirect. As you mentioned it is not implemented, so we would have to emulate the same behaviour from the proxies. A few questions on that:

  • Is there an alternative header that can be set on caddy to have the same effect? Granted I’m new to http language so I don’t know what the intended behaviour is appart for the name and what handle_response should be set to.
  • I believe the internal is set in order to deny access from simple GET requests on that URL directly. What is the recommended setting to achieve the same?

When navigating to /media/... I see that the response header has x-accel-redirect set to a path in _protected. The intended effect is that when navigating there, the identity is checked if they are allowed to get the file or not, and then the file is retrieved from _protected.

This is not implemented yet, but hopefully once I have it sorted, this is essentially what your config would look like, I think:

reverse_proxy localhost:8003 {
	handle_response header X-Accel-Redirect {
		root * /path/to/back/media
		header Content-Disposition "attachment"
		rewrite {http.response.header.X-Accel-Redirect}
		file_server
	}
}

So basically, after the proxy happens, Caddy will check the handle_response matchers – if the response has the X-Accel-Redirect header, it will handle it specially, by rewriting the path and serving a file instead.

Doing it this way, you don’t need “internal”, because it’s implicitly internal, since Caddy allows you to explicitly handle the proxy response.

Anyways, like I said before, handle_response is not yet supported in the Caddyfile. But you can try it right now via JSON config:

2 Likes

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