How to serve pre-compressed files with Caddy v2

I would like to know how to serve pre-compressed files with Caddy v2. Example: when the browser requests the resource /js/app.js and header accept-encoding: gzip, if the resource exists with an extension gz, like /js/app.js.gz, so the response should use the compressed file with the appropriate response header set (content-encoding: gzip).

Right now, the situation is not ideal for this in Caddy, but we do have plans to improve this eventually.

https://github.com/caddyserver/caddy/issues/2665

For now though, the approach to take would be to use a file matcher to see if {path}.gz exists on disk, and if so, handle the request with a handle block that rewrites the request, sets the Content-Type and Content-Encoding. You’ll need to do one of these matcher/handler blocks per file type you want to support because Caddy only looks at the file extension to know the file type and it will only see .gz when determining the content type header to send.

I’m on my phone right now so I can’t type out a full example, but I hope that gets you on the right track.

2 Likes

Thanks. I would appreciate to see the full example.

It would probably look something like this:

@gzippedJS {
	header Accept-Encoding *gzip*
	path *.js
	file {
		try_files {path}.gz
	}
}
handle @gzippedJS {
	header Content-Encoding gzip
	header Content-Type text/javascript
	rewrite {path}.gz
}

You’d have a block like this for each type of file type you have precompressed.

2 Likes

This is the actual Caddyfile, that works partially:

:8383
root * /usr/share/caddy

@gzip {
  header Accept-Encoding *gzip*
  file {
      try_files {path}.gz
  }
}

handle /js/* {
  file_server
  header Content-Type text/javascript

  handle @gzip {
    header Alex compressed
    rewrite {path}.gz
  }
}


handle {
  file_server
  # Support for SPA.
  try_files {path} /index.html
}

The path /js/vendor.js is served from the /js/vendor.js.gz if the browser requests gzip encoding. But the response contains the unknown header Alex: compressed. If I change to the correct header Content-Encoding: gzip, the solution does not work!

In summary, this will NOT work:

handle /js/* {
  file_server
  header Content-Type text/javascript

  handle @gzip {
    header Content-Encoding gzip
    rewrite {path}.gz
  }
}

What’s missing?

What doesn’t work? Just saying “this will NOT work” doesn’t tell us anything. Please elaborate.

It’s reported here: Inconsistent behaviour of handle directive · Issue #3515 · caddyserver/caddy · GitHub

The following Caddyfile is working correctly:

:8383
root * /usr/share/caddy

@gzip {
  header Accept-Encoding *gzip*
  file {
      try_files {path}.gz
  }
}

handle /js/* {
  file_server
  header Content-Type text/javascript
  handle @gzip {
    header Content-Encoding gzip
    rewrite {path}.gz
  }
}


handle {
  file_server
  # Support for SPA.
  try_files {path} /index.html
}

This is the curl I have used for testing it:

curl -H "Accept-Encoding: gzip" -D - http://localhost:8383/js/vendor.js

Caddy correctly served the file /js/vendor.js.gz, and all the response headers are also correct:

Accept-Ranges: bytes
Content-Encoding: gzip
Content-Type: text/javascript
Etag: "qcbnly3ojc"
Last-Modified: Mon, 22 Jun 2020 09:56:22 GMT
Server: Caddy
Date: Mon, 22 Jun 2020 13:59:13 GMT
Transfer-Encoding: chunked

FYI you can simplify that Caddyfile to this, and it should work just the same:

:8383

root * /usr/share/caddy

@gzipJS {
  header Accept-Encoding *gzip*
  file {
      try_files {path}.gz
  }
  path /js/*
}
handle @gzipJS {
  header Content-Type text/javascript
  header Content-Encoding gzip
  rewrite {path}.gz
}

# Support for SPA.
try_files {path} /index.html

file_server  
3 Likes

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