Caddy: Setting headers per file

Created on 17 Jan 2016  路  16Comments  路  Source: caddyserver/caddy

With NGINX I was able to set the header per filetypes in a folder, like

location /tuts (.*\.pdf) {
    add_header Content-disposition "attachment";
}

to ensure files like pdf/mp4 etc are always forced to download, regardless of browser.

I have something like

tuts.example.com {
    root /var/www/static/tuts
    browse
    header / Content-disposition "attachment"
    log ../logs/tuts.log
}

but visiting tuts.example.com just downloads the HTML.

I would like that any file (or preferably specific files) at that location would get that header applied. The folder can have pdf/txt/mp4 files (amongst others), and some will open in the browser and others will download, again depending on the browser used.

The only 100% reliable way with NGINX that I could have a simple indexed page _and_ force certain filetypes to download was to use the add_header method I mentioned above. I also tried changing the mime type to application/octet-stream but that wasn't reliable either.

Is this possible with Caddy or have I just messed up the syntax?

feature request question

Most helpful comment

header is request path based (not OS path based). Maybe try adding mime type too.

tuts.example.com {
    root /var/www/static/tuts
    browse
    header / Content-disposition "attachment"
    mime .mp4 application/octect-stream
    mime .pdf application/octect-stream
}

All 16 comments

With the headers directive, you've set the base path to / which equates to all requests. It should instead be the path to only the folder which has the downloads stored in it.

That's what I had initially but it didn't seem to work at all. I tried just adding a custom header with

header /var/www/static/tuts X-Custom-Header "Some value"

but I couldn't see that in the response in the Chrome dev tools.

If I test a file that I know will download by default, like a zip, then I'll get something like

Accept-Ranges:bytes
Connection:keep-alive
Content-Disposition:attachment
Content-Length:7
Content-Type:application/zip
Date:Mon, 18 Jan 2016 12:23:03 GMT
ETag:"569cd4c7-7"
Last-Modified:Mon, 18 Jan 2016 12:04:23 GMT
Server:nginx/1.4.6 (Ubuntu)

with NGINX, but with Caddy

accept-ranges:bytes
content-length:7
content-type:application/zip
date:Mon, 18 Jan 2016 12:18:05 GMT
last-modified:Mon, 18 Jan 2016 12:04:23 GMT
server:Caddy
status:200

The server blocks are identical between NGINX/Caddy. All I did was change the syntax where needed, like browse instead of autoindex on.

If your root is /var/www/static/, you should set the header for /tuts. It is doing matching based on the HTTP GET path.

The root is /var/www/static/tuts. It's a folder with a bunch of files in it. When someone hits tuts.example.com then I want to serve the contents as a basic indexed page. This much works fine, barring the headers. I have it stripped down to simply

tuts.example.com {
    root /var/www/static/tuts
    browse
    header /var/www/static/tuts Content-disposition "attachment"
}

but no headers get applied.

For testing purposes, I tried making a files folder within tuts and moving the files in there, then used

tuts.example.com {
    root /var/www/static/tuts
    browse
    header /var/www/static/tuts/files Content-disposition "attachment"
}

but no difference.

header is request path based (not OS path based). Maybe try adding mime type too.

tuts.example.com {
    root /var/www/static/tuts
    browse
    header / Content-disposition "attachment"
    mime .mp4 application/octect-stream
    mime .pdf application/octect-stream
}

That was my initial assumption, but when I implement it exactly as you mentioned then visiting tuts.example.com downloads the HTML, it doesn't render the index.

Oh, you mean it downloads every file, including html. But you only want it limited to mp4, pdf e.t.c ?

No, I mean it literally downloads a copy of the HTML. If I go to that address it'll download download.htm. It doesn't actually take me to tuts.example.com.

That's what I've been saying -- the path you set for your header directive should only be the path for files you WANT the browser to download (in your case) -- not the path that includes the html files.

There are no HTML files in that tuts folder. It's just a file dump for txt/zip/mp4/pdfs mostly. That's why I used browse so it would just display as a list of files to choose from to download.

If I use

tuts.example.com {
    root /var/www/static/tuts
    browse
    header /var/www/static/tuts Content-disposition "attachment"
}

then the page renders properly but none of the files get that header applied. If I use

tuts.example.com {
    root /var/www/static/tuts
    browse
    header / Content-disposition "attachment"
}

then it's as I described. The browser just downloads the HTML (i.e the HTML that would be used if the page was to be displayed).

Ohh. So what's happening is you're setting the header for all requests, which tells the browser to download a file no matter what the response is. The browse middleware just writes an HTML response - the browser doesn't know it's not a real file on disk, it just thinks it has to download it because you're setting that header.

That's the problem. Either I get the page as a download, or the page renders as an indexed page but none of the downloadable files get that header applied.

With NGINX my setup was this

server {
    listen   80;

    root /var/www/static/tuts;
    server_name tuts.example.com;

    location ~* \.pdf$ {
        add_header Content-Disposition "attachment";
    }
    autoindex on;
}

Visiting the page would show a list of the files in the tuts folder, much like browse. The crucial difference being that it allowed me to target files inside the folder and apply a header only to them.

If I didn't specify a file(s) and just used

server {
    listen   80;

    root /var/www/static/tuts;
    server_name tuts.example.com;

    add_header Content-Disposition "attachment";

    autoindex on;
}

then it behaves exactly as

tuts.example.com {
    root /var/www/static/tuts
    browse
    header / Content-disposition "attachment"
}

in that it returns an HTML download, not a page listing.

So, as it stands, I can't use browse _and_ target files in that folder to apply headers to? Something like

header /*.pdf Content-disposition "attachment"

Thanks for the help btw. I was able to get as much done in 10 minutes with Caddy as it took me the best part of an hour with NGINX.

Ahhh, I see, so you need to apply headers to just .pdf files.

This can't be done right now -- not intuitively, anyway -- except maybe using a hack with the rewrite directive (@abiosoft?).

Meanwhile I'm considering implementing more powerful request matching features since the number of valid use cases seem to be adding up.

Actually, rewrite can as well be able to add/remove headers since proxy can do that.

Closing since the answer to the question is the same feature request as in a few other related issues. Thanks!

Closing since the answer to the question is the same feature request as in a few other related issues.

I am trying to track down the status of this feature request. Specifically, I want to add headers to a rewrite rule - i.e., if I match a rewrite regex, I want to add response headers.

Is this possible today?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mschneider82 picture mschneider82  路  3Comments

la0wei picture la0wei  路  3Comments

dafanasiev picture dafanasiev  路  3Comments

muhammadmuzzammil1998 picture muhammadmuzzammil1998  路  3Comments

ericmdantas picture ericmdantas  路  3Comments