├── .github └── workflows │ └── main.yaml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── composer.json ├── phpcs.xml ├── phpunit.xml └── src ├── Cache.php ├── CachePrevention.php ├── ClearSiteData.php ├── Expires.php └── expires_defaults.php /.github/workflows/main.yaml: -------------------------------------------------------------------------------- 1 | name: "testing" 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | qa: 11 | name: Quality assurance 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v4 17 | 18 | - name: Validate composer.json and composer.lock 19 | run: composer validate 20 | 21 | - name: Cache Composer packages 22 | id: composer-cache 23 | uses: actions/cache@v4 24 | with: 25 | path: vendor 26 | key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} 27 | restore-keys: | 28 | ${{ runner.os }}-php- 29 | 30 | - name: Install dependencies 31 | if: steps.composer-cache.outputs.cache-hit != 'true' 32 | run: composer install --prefer-dist --no-progress 33 | 34 | - name: Coding Standard 35 | run: composer run-script cs 36 | 37 | tests: 38 | name: Tests 39 | runs-on: ubuntu-latest 40 | 41 | strategy: 42 | matrix: 43 | php: 44 | - 8.1 45 | - 8.2 46 | - 8.3 47 | - 8.4 48 | 49 | steps: 50 | - name: Checkout 51 | uses: actions/checkout@v4 52 | 53 | - name: Install PHP 54 | uses: shivammathur/setup-php@v2 55 | with: 56 | php-version: ${{ matrix.php }} 57 | 58 | - name: Cache PHP dependencies 59 | uses: actions/cache@v4 60 | with: 61 | path: vendor 62 | key: ${{ runner.os }}-php-${{ matrix.php }}-composer-${{ hashFiles('**/composer.json') }} 63 | restore-keys: ${{ runner.os }}-php-${{ matrix.php }}-composer- 64 | 65 | - name: Install dependencies 66 | run: composer install --prefer-dist --no-progress 67 | 68 | - name: Tests 69 | run: composer test 70 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 5 | and this project adheres to [Semantic Versioning](http://semver.org/). 6 | 7 | ## [3.1.0] - 2025-03-21 8 | ### Added 9 | - Support for PHP 8.4 10 | 11 | ## [3.0.0] - 2023-12-17 12 | ### Added 13 | - New test cases for Cache using ETag 14 | 15 | ### Changed 16 | - Replaced read-only `micheh/psr7-cache` with updated `mikespub/micheh-psr7-cache` 17 | - Updated version constraints for dependencies 18 | 19 | ### Removed 20 | - Support for PHP prior to 8.1. 21 | 22 | ## [2.1.0] - 2020-12-02 23 | ### Added 24 | - New middleware `ClearSiteData`. 25 | - Support for PHP 8 26 | 27 | ## [2.0.0] - 2019-12-01 28 | ### Added 29 | - Added a second argument to `Cache` constructor to define a `ResponseFactoryInterface` 30 | 31 | ### Removed 32 | - Support for PHP 7.0 and 7.1 33 | - The `responseFactory()` option in `Cache` middleware. Use the constructor argument. 34 | 35 | ## [1.1.0] - 2018-08-04 36 | ### Added 37 | - PSR-17 support 38 | - New option `responseFactory` 39 | 40 | ## [1.0.0] - 2018-01-25 41 | ### Added 42 | - Improved testing and added code coverage reporting 43 | - Added tests for PHP 7.2 44 | 45 | ### Changed 46 | - Upgraded to the final version of PSR-15 `psr/http-server-middleware` 47 | 48 | ### Fixed 49 | - Updated license year 50 | 51 | ## [0.5.0] - 2017-11-13 52 | ### Changed 53 | - Replaced `http-interop/http-middleware` with `http-interop/http-server-middleware`. 54 | 55 | ### Removed 56 | - Removed support for PHP 5.x. 57 | 58 | ## [0.4.0] - 2017-09-21 59 | ### Changed 60 | - Append `.dist` suffix to phpcs.xml and phpunit.xml files 61 | - Changed the configuration of phpcs and php_cs 62 | - Upgraded phpunit to the latest version and improved its config file 63 | - Updated to `http-interop/http-middleware#0.5` 64 | 65 | ## [0.3.1] - 2017-03-30 66 | ### Fixed 67 | - `Middlewares\Cache` is executed only with `GET` and `HEAD` requests. 68 | 69 | ## [0.3.0] - 2016-12-26 70 | ### Changed 71 | - Updated tests 72 | - Updated to `http-interop/http-middleware#0.4` 73 | - Updated `friendsofphp/php-cs-fixer#2.0` 74 | 75 | ## [0.2.0] - 2016-11-22 76 | ### Changed 77 | - Updated to `http-interop/http-middleware#0.3` 78 | 79 | ## [0.1.0] - 2016-10-03 80 | First version 81 | 82 | [3.1.0]: https://github.com/middlewares/cache/compare/v3.0.0...v3.1.0 83 | [3.0.0]: https://github.com/middlewares/cache/compare/v2.1.0...v3.0.0 84 | [2.1.0]: https://github.com/middlewares/cache/compare/v2.0.0...v2.1.0 85 | [2.0.0]: https://github.com/middlewares/cache/compare/v1.1.0...v2.0.0 86 | [1.1.0]: https://github.com/middlewares/cache/compare/v1.0.0...v1.1.0 87 | [1.0.0]: https://github.com/middlewares/cache/compare/v0.5.0...v1.0.0 88 | [0.5.0]: https://github.com/middlewares/cache/compare/v0.4.0...v0.5.0 89 | [0.4.0]: https://github.com/middlewares/cache/compare/v0.3.1...v0.4.0 90 | [0.3.1]: https://github.com/middlewares/cache/compare/v0.3.0...v0.3.1 91 | [0.3.0]: https://github.com/middlewares/cache/compare/v0.2.0...v0.3.0 92 | [0.2.0]: https://github.com/middlewares/cache/compare/v0.1.0...v0.2.0 93 | [0.1.0]: https://github.com/middlewares/cache/releases/tag/v0.1.0 94 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guide 2 | 3 | This project adheres to [The Code Manifesto](http://codemanifesto.com) as its guidelines for contributor interactions. 4 | 5 | ## The Code Manifesto 6 | 7 | We want to work in an ecosystem that empowers developers to reach their potential--one that encourages growth and effective collaboration. A space that is safe for all. 8 | 9 | A space such as this benefits everyone that participates in it. It encourages new developers to enter our field. It is through discussion and collaboration that we grow, and through growth that we improve. 10 | 11 | In the effort to create such a place, we hold to these values: 12 | 13 | 1. **Discrimination limits us.** This includes discrimination on the basis of race, gender, sexual orientation, gender identity, age, nationality, technology and any other arbitrary exclusion of a group of people. 14 | 2. **Boundaries honor us.** Your comfort levels are not everyone’s comfort levels. Remember that, and if brought to your attention, heed it. 15 | 3. **We are our biggest assets.** None of us were born masters of our trade. Each of us has been helped along the way. Return that favor, when and where you can. 16 | 4. **We are resources for the future.** As an extension of #3, share what you know. Make yourself a resource to help those that come after you. 17 | 5. **Respect defines us.** Treat others as you wish to be treated. Make your discussions, criticisms and debates from a position of respectfulness. Ask yourself, is it true? Is it necessary? Is it constructive? Anything less is unacceptable. 18 | 6. **Reactions require grace.** Angry responses are valid, but abusive language and vindictive actions are toxic. When something happens that offends you, handle it assertively, but be respectful. Escalate reasonably, and try to allow the offender an opportunity to explain themselves, and possibly correct the issue. 19 | 7. **Opinions are just that: opinions.** Each and every one of us, due to our background and upbringing, have varying opinions. That is perfectly acceptable. Remember this: if you respect your own opinions, you should respect the opinions of others. 20 | 8. **To err is human.** You might not intend it, but mistakes do happen and contribute to build experience. Tolerate honest mistakes, and don't hesitate to apologize if you make one yourself. 21 | 22 | ## How to contribute 23 | 24 | This is a collaborative effort. We welcome all contributions submitted as pull requests. 25 | 26 | (Contributions on wording & style are also welcome.) 27 | 28 | ### Bugs 29 | 30 | A bug is a demonstrable problem that is caused by the code in the repository. Good bug reports are extremely helpful – thank you! 31 | 32 | Please try to be as detailed as possible in your report. Include specific information about the environment – version of PHP, etc, and steps required to reproduce the issue. 33 | 34 | ### Pull Requests 35 | 36 | Good pull requests – patches, improvements, new features – are a fantastic help. Before create a pull request, please follow these instructions: 37 | 38 | * The code must follow the [PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md). Run `composer cs-fix` to fix your code before commit. 39 | * Write tests 40 | * Document any change in `README.md` and `CHANGELOG.md` 41 | * One pull request per feature. If you want to do more than one thing, send multiple pull request 42 | 43 | ### Runing tests 44 | 45 | ```sh 46 | composer test 47 | ``` 48 | 49 | To get code coverage information execute the following comand: 50 | 51 | ```sh 52 | composer coverage 53 | ``` 54 | 55 | Then, open the `./coverage/index.html` file in your browser. 56 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019-2025 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # middlewares/cache 2 | 3 | [![Latest Version on Packagist][ico-version]][link-packagist] 4 | [![Software License][ico-license]](LICENSE) 5 | ![Testing][ico-ga] 6 | [![Total Downloads][ico-downloads]][link-downloads] 7 | 8 | Middleware components with the following cache utilities: 9 | 10 | * [CachePrevention](#cacheprevention) 11 | * [Expires](#expires) 12 | * [Cache](#cache) 13 | * [ClearSiteData](#clearsitedata) 14 | 15 | ## Requirements 16 | 17 | * PHP >= 8.1 18 | * A [PSR-7 http library](https://github.com/middlewares/awesome-psr15-middlewares#psr-7-implementations) 19 | * A [PSR-15 middleware dispatcher](https://github.com/middlewares/awesome-psr15-middlewares#dispatcher) 20 | 21 | ## Installation 22 | 23 | This package is installable and autoloadable via Composer as [middlewares/cache](https://packagist.org/packages/middlewares/cache). 24 | 25 | ```sh 26 | composer require middlewares/cache 27 | ``` 28 | 29 | ## CachePrevention 30 | 31 | To add the response headers for cache prevention. Useful in development environments: 32 | 33 | ```php 34 | Dispatcher::run([ 35 | new Middlewares\CachePrevention() 36 | ]); 37 | ``` 38 | 39 | ## Expires 40 | 41 | This middleware adds the `Expires` and `Cache-Control: max-age` headers to the response. You can configure the cache duration for each mimetype. If it's not defined, [use the defaults](src/expires_defaults.php). 42 | 43 | ```php 44 | // Use the default configuration 45 | $expires = new Middlewares\Expires(); 46 | 47 | // Custom durations 48 | $expires = new Middlewares\Expires([ 49 | 'text/css' => '+1 year', 50 | 'text/js' => '+1 week', 51 | ]); 52 | ``` 53 | 54 | ### defaultExpires 55 | 56 | Set the default expires value if the request mimetype is not configured. By default is 1 month. Example: 57 | 58 | ```php 59 | //set 1 year lifetime to css and js 60 | $durations = [ 61 | 'text/css' => '+1 year', 62 | 'text/javascript' => '+1 year', 63 | ]; 64 | 65 | //and 1 hour to everything else 66 | $default = '+1 hour'; 67 | 68 | $expires = (new Middlewares\Expires($durations))->defaultExpires($default); 69 | ``` 70 | 71 | ## Cache 72 | 73 | Saves the response headers in a [PSR-6 cache pool](http://www.php-fig.org/psr/psr-6/) and returns `304` responses (Not modified) if the response is still valid (based on its `ETag` or `Last-Modified` header). This saves server resources and bandwidth because the body is returned empty. It's recomended to combine it with `Expires` to set the lifetime of the responses. 74 | 75 | ```php 76 | $cachePool = new Psr6CachePool(); 77 | 78 | Dispatcher::run([ 79 | new Middlewares\Cache($cachePool), 80 | new Middlewares\Expires() 81 | ]); 82 | ``` 83 | 84 | Optionally, you can provide a `Psr\Http\Message\ResponseFactoryInterface` as the second argument to create the `304` empty responses. If it's not defined, [Middleware\Utils\Factory](https://github.com/middlewares/utils#factory) will be used to detect it automatically. 85 | 86 | ```php 87 | $cachePool = new Psr6CachePool(); 88 | $responseFactory = new MyOwnResponseFactory(); 89 | 90 | $cache = new Middlewares\Cache($cachePool, $responseFactory); 91 | ``` 92 | 93 | ## ClearSiteData 94 | 95 | Send the header `Clear-Site-Data` to remove all site data in the client (cache, cookies, storage etc) 96 | 97 | ```php 98 | Dispatcher::run([ 99 | new Middlewares\ClearSiteData() 100 | ]); 101 | ``` 102 | 103 | Optionally, you can provide the list of data types that you want to clear. 104 | 105 | ```php 106 | $cache = new Middlewares\ClearSiteData('cache', 'cookies'); 107 | ``` 108 | 109 | --- 110 | 111 | Please see [CHANGELOG](CHANGELOG.md) for more information about recent changes and [CONTRIBUTING](CONTRIBUTING.md) for contributing details. 112 | 113 | The MIT License (MIT). Please see [LICENSE](LICENSE) for more information. 114 | 115 | [ico-version]: https://img.shields.io/packagist/v/middlewares/cache.svg?style=flat-square 116 | [ico-license]: https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square 117 | [ico-ga]: https://github.com/middlewares/cache/workflows/testing/badge.svg 118 | [ico-downloads]: https://img.shields.io/packagist/dt/middlewares/cache.svg?style=flat-square 119 | 120 | [link-packagist]: https://packagist.org/packages/middlewares/cache 121 | [link-downloads]: https://packagist.org/packages/middlewares/cache 122 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "middlewares/cache", 3 | "type": "library", 4 | "description": "Middleware with various cache utilities", 5 | "license": "MIT", 6 | "keywords": [ 7 | "psr-7", 8 | "psr-15", 9 | "middleware", 10 | "http" 11 | ], 12 | "homepage": "https://github.com/middlewares/cache", 13 | "support": { 14 | "issues": "https://github.com/middlewares/cache/issues" 15 | }, 16 | "require": { 17 | "php": ">=8.1", 18 | "middlewares/utils": "^4", 19 | "mikespub/micheh-psr7-cache": "^2.0", 20 | "psr/cache": "^1.0 || ^2.0 || ^3.0", 21 | "psr/http-server-middleware": "^1" 22 | }, 23 | "require-dev": { 24 | "phpunit/phpunit": "^10", 25 | "friendsofphp/php-cs-fixer": "^3", 26 | "squizlabs/php_codesniffer": "^3", 27 | "oscarotero/php-cs-fixer-config": "^2", 28 | "matthiasmullie/scrapbook": "^1.5", 29 | "phpstan/phpstan": "^2", 30 | "nyholm/psr7": "^1.8", 31 | "nyholm/psr7-server": "^1.1" 32 | }, 33 | "autoload": { 34 | "psr-4": { 35 | "Middlewares\\": "src/" 36 | } 37 | }, 38 | "autoload-dev": { 39 | "psr-4": { 40 | "Middlewares\\Tests\\": "tests/" 41 | } 42 | }, 43 | "scripts": { 44 | "cs": "phpcs", 45 | "cs-fix": "php-cs-fixer fix", 46 | "phpstan": "phpstan analyse", 47 | "test": "phpunit", 48 | "coverage": "phpunit --coverage-text", 49 | "coverage-html": "phpunit --coverage-html=coverage" 50 | } 51 | } -------------------------------------------------------------------------------- /phpcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Middlewares coding standard 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | src 15 | tests 16 | 17 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | tests 7 | 8 | 9 | 10 | 11 | ./src 12 | 13 | 14 | ./tests 15 | ./vendor 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/Cache.php: -------------------------------------------------------------------------------- 1 | cache = $cache; 33 | $this->responseFactory = $responseFactory ?: Factory::getResponseFactory(); 34 | } 35 | 36 | /** 37 | * Process a request and return a response. 38 | */ 39 | public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface 40 | { 41 | //Only GET & HEAD request 42 | if (!in_array($request->getMethod(), ['GET', 'HEAD'], true)) { 43 | return $handler->handle($request); 44 | } 45 | 46 | $util = new CacheUtil(); 47 | $key = $request->getMethod().md5((string) $request->getUri()); 48 | $item = $this->cache->getItem($key); 49 | 50 | //It's cached 51 | if ($item->isHit()) { 52 | $headers = $item->get(); 53 | $response = $this->responseFactory->createResponse(304); 54 | 55 | foreach ($headers as $name => $header) { 56 | $response = $response->withHeader($name, $header); 57 | } 58 | 59 | if ($util->isNotModified($request, $response)) { 60 | return $response; 61 | } 62 | 63 | $this->cache->deleteItem($key); 64 | } 65 | 66 | $response = $handler->handle($request); 67 | 68 | if (!$response->hasHeader('Last-Modified')) { 69 | $response = $util->withLastModified($response, time()); 70 | } 71 | 72 | //Save in the cache 73 | if ($util->isCacheable($response)) { 74 | $item->set($response->getHeaders()); 75 | $item->expiresAfter($util->getLifetime($response)); 76 | 77 | $this->cache->save($item); 78 | } 79 | 80 | return $response; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/CachePrevention.php: -------------------------------------------------------------------------------- 1 | handle($request); 20 | 21 | $util = new CacheUtil(); 22 | 23 | return $util->withCachePrevention($response); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/ClearSiteData.php: -------------------------------------------------------------------------------- 1 | types = $types; 31 | } 32 | } 33 | 34 | /** 35 | * Process a request and return a response. 36 | */ 37 | public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface 38 | { 39 | $response = $handler->handle($request); 40 | 41 | $types = array_map(function ($type) { 42 | return "\"{$type}\""; 43 | }, $this->types); 44 | 45 | $value = implode(' ', $types); 46 | 47 | return $response->withHeader('Clear-Site-Data', $value); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Expires.php: -------------------------------------------------------------------------------- 1 | 23 | */ 24 | private $expires; 25 | 26 | /** 27 | * Define de available expires. 28 | * 29 | * @param array|null $expires 30 | */ 31 | public function __construct(?array $expires = null) 32 | { 33 | $this->expires = $expires ?: require __DIR__.'/expires_defaults.php'; 34 | } 35 | 36 | /** 37 | * Set the default expires value. 38 | */ 39 | public function defaultExpires(string $expires): self 40 | { 41 | $this->default = $expires; 42 | 43 | return $this; 44 | } 45 | 46 | /** 47 | * Process a request and return a response. 48 | */ 49 | public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface 50 | { 51 | $response = $handler->handle($request); 52 | 53 | //Only GET & HEAD request 54 | if (!in_array($request->getMethod(), ['GET', 'HEAD'], true)) { 55 | return $response; 56 | } 57 | 58 | $util = new CacheUtil(); 59 | 60 | //Only cacheable responses 61 | if (!$util->isCacheable($response)) { 62 | return $response; 63 | } 64 | 65 | //Check if a lifetime is defined 66 | $lifetime = $util->getLifetime($response); 67 | 68 | if ($lifetime !== null) { 69 | return self::withExpires($response, $util, '@'.(time() + $lifetime)); 70 | } 71 | 72 | //Check the content type 73 | $contentType = $response->getHeaderLine('Content-Type'); 74 | 75 | if ($contentType !== '') { 76 | foreach ($this->expires as $mime => $time) { 77 | if (stripos($contentType, $mime) !== false) { 78 | return self::withExpires($response, $util, $time); 79 | } 80 | } 81 | } 82 | 83 | //Add default value 84 | return self::withExpires($response, $util, $this->default); 85 | } 86 | 87 | /** 88 | * Add the Expires and Cache-Control headers. 89 | */ 90 | private static function withExpires( 91 | ResponseInterface $response, 92 | CacheUtil $util, 93 | string $expires 94 | ): ResponseInterface { 95 | $expires = new DateTime($expires); 96 | $cacheControl = ResponseCacheControl::fromString($response->getHeaderLine('Cache-Control')) 97 | ->withMaxAge($expires->getTimestamp() - time()); 98 | 99 | $response = $util->withExpires($response, $expires); 100 | 101 | // @phpstan-ignore-next-line 102 | return $util->withCacheControl($response, $cacheControl); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/expires_defaults.php: -------------------------------------------------------------------------------- 1 | '+1 year', 6 | 'application/atom+xml' => '+1 hour', 7 | 'application/rdf+xml' => '+1 hour', 8 | 'application/rss+xml' => '+1 hour', 9 | 'application/json' => '+0 seconds', 10 | 'application/ld+json' => '+0 seconds', 11 | 'application/schema+json' => '+0 seconds', 12 | 'application/vnd.geo+json' => '+0 seconds', 13 | 'application/xml' => '+0 seconds', 14 | 'text/xml' => '+0 seconds', 15 | 'image/vnd.microsoft.icon' => '+1 week', 16 | 'image/x-icon' => '+1 week', 17 | 'text/html' => '+0 seconds', 18 | 'application/javascript' => '+1 year', 19 | 'application/x-javascript' => '+1 year', 20 | 'text/javascript' => '+1 year', 21 | 'application/manifest+json' => '+1 week', 22 | 'application/x-web-app-manifest+json' => '+0 seconds', 23 | 'text/cache-manifest' => '+0 seconds', 24 | 'audio/ogg' => '+1 month', 25 | 'image/bmp' => '+1 month', 26 | 'image/gif' => '+1 month', 27 | 'image/jpeg' => '+1 month', 28 | 'image/png' => '+1 month', 29 | 'image/svg+xml' => '+1 month', 30 | 'image/webp' => '+1 month', 31 | 'video/mp4' => '+1 month', 32 | 'video/ogg' => '+1 month', 33 | 'video/webm' => '+1 month', 34 | 'application/vnd.ms-fontobject' => '+1 month', 35 | 'font/eot' => '+1 month', 36 | 'font/opentype' => '+1 month', 37 | 'application/x-font-ttf' => '+1 month', 38 | 'application/font-woff' => '+1 month', 39 | 'application/x-font-woff' => '+1 month', 40 | 'font/woff' => '+1 month', 41 | 'application/font-woff2' => '+1 month', 42 | 'text/x-cross-domain-policy' => '+1 week', 43 | ]; 44 | --------------------------------------------------------------------------------