Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

[Feature] Serve pre-compressed static files (.br, .gz)

Open
#1,007 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
55/100
Issue type
Feature
Clarity
Mostly clear
Activity status
Active
Tech stack
php
Domain
backend

Research direction

Start with src/Middleware/StaticFilesMiddleware.php, especially the response logic at lines 112 and 121-125, generateEtag() at lines 157-160, and the realpath cache key at line 294. Trace the existing block checks and file response behavior before adding coverage for encoding negotiation, content type, ETags, missing originals, and blocked x.php.gz files. Done includes the default [] behavior and updates to docs/middlewares.md and docs/security.md.

Written by the indexing model from the issue text.

Description

priority:low type:feature

Problem

StaticFilesMiddleware always sends the plain file. It never looks for a compressed copy.

  • src/Middleware/StaticFilesMiddleware.php:121-125: the response is withFile($filePath) with Last-Modified, ETag and Cache-Control. There is no Content-Encoding and no Vary header.
  • src/Middleware/StaticFilesMiddleware.php:112: when filemtime() fails, it also sends the plain file.
  • grep -rni "accept-encoding\|content-encoding\|gzip" src finds nothing.
  • Build tools (Vite, Webpack) can write app.js.br and app.js.gz next to app.js. Today the worker does not use these files. It sends the big file.

Most production setups serve static files from nginx, Caddy or a CDN. So this helps mostly small setups, or setups with only one container.

Idea

A new constructor argument precompressed for StaticFilesMiddleware:

services:
  app.static_files:
    class: CrazyGoat\WorkermanBundle\Middleware\StaticFilesMiddleware
    arguments:
      $rootDirectory: '%kernel.project_dir%/public'
      $precompressed: ['br', 'gzip']

For a request to /build/app.js:

  1. Read Accept-Encoding from the request.
  2. Check the list in order: br -> app.js.br, gzip -> app.js.gz.
  3. If the client accepts the encoding and the file is there, send it with Content-Encoding: br (or gzip).
  4. When the option is on, always send Vary: Accept-Encoding.
  5. Else send the plain file, like today.

The default is []. So nothing changes for current users.

Seen in

  • Caddy file_server { precompressed br gzip } (also used by FrankenPHP)
  • nginx gzip_static on and brotli_static on

Risks

  • Content-Type: Workerman takes the type from the extension of the sent file (Response::createHeadForFile()). For app.js.br the type is wrong. If the type is application/octet-stream, Workerman also adds Content-Disposition: attachment. The middleware must set Content-Type from the original name.
  • ETag: generateEtag() (StaticFilesMiddleware.php:157-160) uses only mtime and path. Each encoding needs its own ETag (for example a -br suffix). Else caches can mix the files.
  • The realpath cache key (StaticFilesMiddleware.php:294) must include the variant.
  • The .br and .gz files must pass the same block checks as the plain file. gz is not in the block lists, but x.php.gz is blocked today (LEAK_EXTENSIONS, line 13). This must stay so.
  • The plain file must exist too. Do not send a .gz file when there is no plain file.
  • It is better to fix the open static file issues first: #977, #940, #904, #903.

Done when

  • A test shows: with Accept-Encoding: br, gzip and app.js.br on disk, the body is the .br file, with Content-Encoding: br, Vary: Accept-Encoding and the Content-Type for .js.
  • A test shows: without Accept-Encoding, the plain file is sent.
  • A test shows: the ETag of the .br answer is not the same as the ETag of the plain answer.
  • A test shows: a .gz file with no plain file next to it is not sent.
  • A test shows: x.php.gz is still blocked.
  • The default [] keeps the old behaviour.
  • docs/middlewares.md and docs/security.md describe the option.
Dominant language
PHP
Stars
2
Forks
3
Avg merge
7h 15m
Merged PRs (30d)
97

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from crazy-goat/workerman-bundle

All issues in crazy-goat/workerman-bundle

Similar issues

More PHP issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.