├── .gitignore ├── views ├── domain_sharding.htm ├── gzip.htm └── caching.htm ├── models ├── settings │ ├── _sharding_warning.htm │ └── fields.yaml └── Settings.php ├── composer.json ├── classes ├── htaccess │ ├── HtaccessManager.php │ └── HtaccessWriter.php └── middleware │ ├── CDNMiddleware.php │ └── Http2Middleware.php ├── console ├── StatusCommand.php ├── ToggleGzipCommand.php ├── ToggleCachingCommand.php └── ToggleHttp2Command.php ├── LICENSE ├── updates └── version.yaml ├── README.md ├── Plugin.php └── lang ├── en └── lang.php ├── pt-pt └── lang.php ├── ru └── lang.php ├── de └── lang.php └── fr └── lang.php /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | -------------------------------------------------------------------------------- /views/domain_sharding.htm: -------------------------------------------------------------------------------- 1 | 2 | ## To enable proper Cross domain requests we need 3 | ## to set the proper headers 4 | 5 | Header set Access-Control-Allow-Origin "*" 6 | 7 | -------------------------------------------------------------------------------- /models/settings/_sharding_warning.htm: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |

5 |

6 |
7 |
8 |

9 | 10 |

11 |
12 |
-------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "offline/oc-speedy-plugin", 3 | "type": "october-plugin", 4 | "description": "Speed up your October CMS installation", 5 | "keywords": [ 6 | "october", 7 | "cms", 8 | "speed", 9 | "optimization", 10 | "seo" 11 | ], 12 | "license": "MIT", 13 | "authors": [ 14 | { 15 | "name": "Tobias Kündig", 16 | "email": "tobias@offline.ch" 17 | } 18 | ], 19 | "require": { 20 | "composer/installers": "~1.0", 21 | "symfony/dom-crawler": "~2.7|~3.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /classes/htaccess/HtaccessManager.php: -------------------------------------------------------------------------------- 1 | manager = $manager ?: new HtaccessWriter(); 14 | } 15 | 16 | public function toggleSection($section, $status) 17 | { 18 | if ($status === true) { 19 | $this->manager->writeSection($section); 20 | } else { 21 | $this->manager->removeSection($section); 22 | } 23 | } 24 | 25 | public function save() 26 | { 27 | return $this->manager->writeContents(); 28 | } 29 | } -------------------------------------------------------------------------------- /console/StatusCommand.php: -------------------------------------------------------------------------------- 1 | table($headers, $values); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 OFFLINE GmbH 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 | -------------------------------------------------------------------------------- /updates/version.yaml: -------------------------------------------------------------------------------- 1 | 1.0.1: Initial version of Speedy 2 | 1.0.2: Fixed compatibility issues with builds 420+ 3 | 1.0.3: Added russian translations (thanks to pavalx-ru) 4 | 1.0.4: Added french translations (thanks to damsfx) 5 | 1.0.5: Added pt-pt translations (thanks to João Costa) 6 | 1.0.6: Switched to mod_deflate for gzip compression. Disable gzip compression in the Speedy settings and turn it back on to apply these changes to your installation. 7 | 1.0.7: Increased Cache-Control header to a max-age value of 1 year for images, fonts and videos to be in line with Google's current recommendations. Disable and re-enable the caching option to benefit from these changes. (https://web.dev/uses-long-cache-ttl/) 8 | 1.0.8: Optimized caching of woff2 fonts 9 | 1.0.9: Added Composer installation support 10 | 1.0.10: "Added console commands to enable optimizations (thanks to @guus-frenken)" 11 | 1.0.11: "Modified console commands to toggle optimizations (thanks to @guus-frenken)" 12 | 1.0.12: "Fixed compatibility issue in CDNMiddleware with newer Laravel versions" 13 | 1.0.13: "Optimized CDNMiddleware: Don't fetch Theme settings if the middleware is disabled" 14 | 15 | -------------------------------------------------------------------------------- /views/gzip.htm: -------------------------------------------------------------------------------- 1 | 2 | AddOutputFilterByType DEFLATE text/plain 3 | AddOutputFilterByType DEFLATE text/html 4 | AddOutputFilterByType DEFLATE text/xml 5 | AddOutputFilterByType DEFLATE text/shtml 6 | AddOutputFilterByType DEFLATE text/css 7 | AddOutputFilterByType DEFLATE application/xml 8 | AddOutputFilterByType DEFLATE application/xhtml+xml 9 | AddOutputFilterByType DEFLATE application/rss+xml 10 | AddOutputFilterByType DEFLATE application/javascript 11 | AddOutputFilterByType DEFLATE application/x-javascript 12 | 13 | # Fonts 14 | AddOutputFilterByType DEFLATE application/vnd.ms-fontobject 15 | AddOutputFilterByType DEFLATE application/x-font 16 | AddOutputFilterByType DEFLATE application/x-font-opentype 17 | AddOutputFilterByType DEFLATE application/x-font-otf 18 | AddOutputFilterByType DEFLATE application/x-font-truetype 19 | AddOutputFilterByType DEFLATE application/x-font-ttf 20 | AddOutputFilterByType DEFLATE font/opentype 21 | AddOutputFilterByType DEFLATE x-font/ttf 22 | AddOutputFilterByType DEFLATE x-font/otf 23 | 24 | AddType x-font/otf .otf 25 | AddType x-font/ttf .ttf 26 | AddType x-font/eot .eot 27 | AddType x-font/woff .woff 28 | 29 | -------------------------------------------------------------------------------- /models/Settings.php: -------------------------------------------------------------------------------- 1 | value; 25 | 26 | return array_key_exists($value, $values) 27 | ? (bool)$values[$value] 28 | : false; 29 | } 30 | 31 | $enableCaching = key('enable_caching', $setting); 32 | $enableGzip = key('enable_gzip', $setting); 33 | $enableDomainSharding = key('enable_domain_sharding', $setting); 34 | 35 | $htaccess = new HtaccessManager(); 36 | $htaccess->toggleSection('caching', $enableCaching); 37 | $htaccess->toggleSection('gzip', $enableGzip); 38 | $htaccess->toggleSection('domain_sharding', $enableDomainSharding); 39 | $htaccess->save(); 40 | }); 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Speedy plugin 2 | Website optimization plugin for October CMS. 3 | 4 | ## Optimizations 5 | 6 | Speedy provides you with the following optimization options. You can enable and disable them via the backend settings. 7 | 8 | * HTTP/2 preloading 9 | * Gzip 10 | * Cache headers 11 | * Domain sharding 12 | 13 | ## Requirements 14 | 15 | Speedy currently only works with the Apache web server with enabled `htaccess` file support. 16 | 17 | Speedy makes use of `mod_expires`, `mod_gzip` and `mod_headers`. 18 | 19 | ## Console commands 20 | 21 | You can get your current Speedy settings with the following command: 22 | 23 | ```bash 24 | php artisan speedy:status 25 | ``` 26 | 27 | There are also a number of console commands to toggle optimizations. 28 | 29 | **Toggle HTTP/2 preloading:** 30 | 31 | ```bash 32 | php artisan speedy:toggle-http2 33 | ``` 34 | 35 | **Toggle Gzip:** 36 | 37 | ```bash 38 | php artisan speedy:toggle-gzip 39 | ``` 40 | 41 | **Toggle Caching:** 42 | 43 | ```bash 44 | php artisan speedy:toggle-caching 45 | ``` 46 | 47 | You can also choose to try to enable to disable an option by adding the `--enable` or `--disable` option respectively to any of the toggle commands. If the option is already enabled / disabled, no changes will be made. 48 | 49 | ## Attributions 50 | 51 | The speedy flash icon was created by [SagarUnagar](https://www.iconfinder.com/SagarUnagar) and is licensed under [CC BY 3.0](https://creativecommons.org/licenses/by/3.0/). Speedy uses a modified version of [JacobBennett's laravel-HTTP2ServerPush](https://github.com/JacobBennett/laravel-HTTP2ServerPush) middleware which is licensed under the [MIT license](https://github.com/JacobBennett/laravel-HTTP2ServerPush/blob/master/LICENSE.md). 52 | -------------------------------------------------------------------------------- /classes/middleware/CDNMiddleware.php: -------------------------------------------------------------------------------- 1 | isDisabled() || $this->disableBecauseInDebug()) { 26 | return $response; 27 | } 28 | 29 | $theme = Theme::getActiveTheme(); 30 | if (!$theme) { 31 | return $response; 32 | } 33 | 34 | $themeDir = $theme->getDirName(); 35 | $themePath = Config::get('cms.themesPath', '/themes') . '/' . $themeDir; 36 | 37 | $baseUrl = url()->to('/'); 38 | 39 | $cdnUrl = trim(Settings::get('domain_sharding_cdn', ''), '/'); 40 | $themePath = trim($themePath, '/'); 41 | 42 | $contents = $response->getContent(); 43 | 44 | $replacements = [ 45 | $baseUrl . '/' . $themePath => $cdnUrl . '/' . $themePath, 46 | ]; 47 | 48 | foreach ($replacements as $from => $to) { 49 | $replaced = str_replace($from, $to, $contents); 50 | } 51 | 52 | $response->setContent($replaced); 53 | 54 | return $response; 55 | } 56 | 57 | protected function disableBecauseInDebug() 58 | { 59 | if (!config('app.debug')) { 60 | return false; 61 | } 62 | 63 | return (bool)Settings::get('enable_domain_sharding_in_debug', false) !== true; 64 | } 65 | 66 | protected function isDisabled() 67 | { 68 | return (bool)Settings::get('enable_domain_sharding', false) !== true; 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /console/ToggleGzipCommand.php: -------------------------------------------------------------------------------- 1 | option('enable')) { 22 | if (SpeedySettings::get('enable_gzip')) { 23 | $this->info('Gzip is already enabled.'); 24 | 25 | return; 26 | } 27 | 28 | SpeedySettings::set(['enable_gzip' => true]); 29 | 30 | $this->output->success('Gzip is now enabled.'); 31 | 32 | return; 33 | } 34 | 35 | if ($this->option('disable')) { 36 | if ( ! SpeedySettings::get('enable_gzip')) { 37 | $this->info('Gzip is already disabled.'); 38 | 39 | return; 40 | } 41 | 42 | SpeedySettings::set(['enable_gzip' => false]); 43 | 44 | $this->output->success('Gzip is now disabled.'); 45 | 46 | return; 47 | } 48 | 49 | 50 | if (SpeedySettings::get('enable_gzip')) { 51 | if ($this->confirm('Gzip is currently enabled, do you wish to disable it?')) { 52 | SpeedySettings::set(['enable_gzip' => false]); 53 | 54 | $this->output->success('Gzip is now disabled.'); 55 | } 56 | } else { 57 | if ($this->confirm('Gzip is currently disabled, do you wish to enable it?')) { 58 | SpeedySettings::set(['enable_gzip' => true]); 59 | 60 | $this->output->success('Gzip is now enabled.'); 61 | } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /console/ToggleCachingCommand.php: -------------------------------------------------------------------------------- 1 | option('enable')) { 22 | if (SpeedySettings::get('enable_caching')) { 23 | $this->info('Caching is already enabled.'); 24 | 25 | return; 26 | } 27 | 28 | SpeedySettings::set(['enable_caching' => true]); 29 | 30 | $this->output->success('Caching is now enabled.'); 31 | 32 | return; 33 | } 34 | 35 | if ($this->option('disable')) { 36 | if ( ! SpeedySettings::get('enable_caching')) { 37 | $this->info('Caching is already disabled.'); 38 | 39 | return; 40 | } 41 | 42 | SpeedySettings::set(['enable_caching' => false]); 43 | 44 | $this->output->success('Caching is now disabled.'); 45 | 46 | return; 47 | } 48 | 49 | 50 | if (SpeedySettings::get('enable_caching')) { 51 | if ($this->confirm('Caching is currently enabled, do you wish to disable it?')) { 52 | SpeedySettings::set(['enable_caching' => false]); 53 | 54 | $this->output->success('Caching is now disabled.'); 55 | } 56 | } else { 57 | if ($this->confirm('Caching is currently disabled, do you wish to enable it?')) { 58 | SpeedySettings::set(['enable_caching' => true]); 59 | 60 | $this->output->success('Caching is now enabled.'); 61 | } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /models/settings/fields.yaml: -------------------------------------------------------------------------------- 1 | fields: 2 | quick_hacks: 3 | label: offline.speedy::lang.settings.quick_hacks 4 | comment: offline.speedy::lang.settings.quick_hacks_comment 5 | type: section 6 | 7 | enable_http2: 8 | span: left 9 | type: switch 10 | label: offline.speedy::lang.settings.enable_http2 11 | comment: offline.speedy::lang.settings.enable_http2_comment 12 | default: false 13 | 14 | enable_gzip: 15 | span: left 16 | type: switch 17 | label: offline.speedy::lang.settings.enable_gzip 18 | comment: offline.speedy::lang.settings.enable_gzip_comment 19 | default: false 20 | 21 | enable_caching: 22 | span: left 23 | type: switch 24 | label: offline.speedy::lang.settings.enable_caching 25 | comment: offline.speedy::lang.settings.enable_caching_comment 26 | default: false 27 | 28 | domain_sharding_section: 29 | label: offline.speedy::lang.settings.domain_sharding_section 30 | comment: offline.speedy::lang.settings.domain_sharding_section_comment 31 | type: section 32 | 33 | enable_domain_sharding: 34 | span: left 35 | type: switch 36 | label: offline.speedy::lang.settings.enable_domain_sharding 37 | comment: offline.speedy::lang.settings.enable_domain_sharding_comment 38 | default: false 39 | 40 | enable_domain_sharding_in_debug: 41 | span: right 42 | type: switch 43 | label: offline.speedy::lang.settings.enable_domain_sharding_in_debug 44 | comment: offline.speedy::lang.settings.enable_domain_sharding_in_debug_comment 45 | default: false 46 | 47 | domain_sharding_warning: 48 | type: partial 49 | span: right 50 | path: $/offline/speedy/models/settings/_sharding_warning.htm 51 | 52 | domain_sharding_cdn: 53 | type: text 54 | label: offline.speedy::lang.settings.domain_sharding_cdn_domain 55 | comment: offline.speedy::lang.settings.domain_sharding_cdn_domain_comment 56 | span: left 57 | default: http://cdn.yourwebsite.com -------------------------------------------------------------------------------- /views/caching.htm: -------------------------------------------------------------------------------- 1 | 2 | ExpiresActive on 3 | 4 | # Perhaps better to whitelist expires rules? Perhaps. 5 | ExpiresDefault "access plus 1 month" 6 | 7 | # cache.appcache needs re-requests in FF 3.6 (thx Remy ~Introducing HTML5) 8 | ExpiresByType text/cache-manifest "access plus 0 seconds" 9 | 10 | # Your document html 11 | ExpiresByType text/html "access plus 0 seconds" 12 | 13 | # Data 14 | ExpiresByType text/xml "access plus 0 seconds" 15 | ExpiresByType application/xml "access plus 0 seconds" 16 | ExpiresByType application/json "access plus 0 seconds" 17 | 18 | # RSS feed 19 | ExpiresByType application/rss+xml "access plus 1 hour" 20 | 21 | # Favicon (cannot be renamed) 22 | ExpiresByType image/x-icon "access plus 1 week" 23 | 24 | # Media: images, video, audio 25 | ExpiresByType image/gif "access plus 1 year" 26 | ExpiresByType image/png "access plus 1 year" 27 | ExpiresByType image/jpg "access plus 1 year" 28 | ExpiresByType image/jpeg "access plus 1 year" 29 | ExpiresByType image/webp "access plus 1 year" 30 | ExpiresByType video/ogg "access plus 1 year" 31 | ExpiresByType video/webm "access plus 1 year" 32 | ExpiresByType video/mp4 "access plus 1 year" 33 | ExpiresByType audio/ogg "access plus 1 year" 34 | # HTC files (css3pie) 35 | ExpiresByType text/x-component "access plus 1 month" 36 | 37 | # Webfonts 38 | ExpiresByType font/truetype "access plus 1 year" 39 | ExpiresByType font/opentype "access plus 1 year" 40 | ExpiresByType font/woff2 "access plus 1 year" 41 | ExpiresByType application/x-font-woff "access plus 1 year" 42 | ExpiresByType image/svg+xml "access plus 1 year" 43 | ExpiresByType application/vnd.ms-fontobject "access plus 1 year" 44 | 45 | # CSS and JavaScript 46 | ExpiresByType text/css "access plus 1 year" 47 | ExpiresByType application/javascript "access plus 1 year" 48 | ExpiresByType text/javascript "access plus 1 year" 49 | 50 | 51 | Header append Cache-Control "public" 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /console/ToggleHttp2Command.php: -------------------------------------------------------------------------------- 1 | option('enable')) { 22 | if (SpeedySettings::get('http2_enabled')) { 23 | $this->info('HTTP/2 preloading is already enabled.'); 24 | 25 | return; 26 | } 27 | 28 | SpeedySettings::set(['http2_enabled' => true]); 29 | 30 | $this->output->success('HTTP/2 preloading is now enabled.'); 31 | 32 | return; 33 | } 34 | 35 | if ($this->option('disable')) { 36 | if ( ! SpeedySettings::get('http2_enabled')) { 37 | $this->info('HTTP/2 preloading is already disabled.'); 38 | 39 | return; 40 | } 41 | 42 | SpeedySettings::set(['http2_enabled' => false]); 43 | 44 | $this->output->success('HTTP/2 preloading is now disabled.'); 45 | 46 | return; 47 | } 48 | 49 | 50 | if (SpeedySettings::get('http2_enabled')) { 51 | if ($this->confirm('HTTP/2 preloading is currently enabled, do you wish to disable it?')) { 52 | SpeedySettings::set(['http2_enabled' => false]); 53 | 54 | $this->output->success('HTTP/2 preloading is now disabled.'); 55 | } 56 | } else { 57 | if ($this->confirm('HTTP/2 preloading is currently disabled, do you wish to enable it?')) { 58 | SpeedySettings::set(['http2_enabled' => true]); 59 | 60 | $this->output->success('HTTP/2 preloading is now enabled.'); 61 | } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /classes/htaccess/HtaccessWriter.php: -------------------------------------------------------------------------------- 1 | contents = file_get_contents($htaccess); 21 | $this->path = $htaccess; 22 | } 23 | 24 | public function writeSection($section) 25 | { 26 | if ($this->hasSection($section)) { 27 | return; 28 | } 29 | 30 | $view = 'offline.speedy::' . $section; 31 | 32 | if ( ! View::exists($view)) { 33 | throw new RuntimeException('Cannot find htaccess template for section ' . $section); 34 | } 35 | 36 | $template = View::make($view)->render(); 37 | 38 | $this->appendContents($template, $section); 39 | } 40 | 41 | public function removeSection($section) 42 | { 43 | $this->contents = preg_replace( 44 | $this->sectionRegex($section), 45 | '', 46 | $this->contents 47 | ); 48 | } 49 | 50 | public function hasSection($section) 51 | { 52 | $section = preg_quote($section, '/'); 53 | 54 | return (bool)preg_match_all( 55 | $this->sectionRegex($section), 56 | $this->contents 57 | ); 58 | } 59 | 60 | public function writeContents() 61 | { 62 | return file_put_contents($this->path, $this->contents); 63 | } 64 | 65 | protected function appendContents($contents, $section) 66 | { 67 | $append = []; 68 | $append[] = "## START OFFLINE.Speedy - ${section}"; 69 | $append[] = '# DO NOT REMOVE THESE LINES'; 70 | $append[] = $contents; 71 | $append[] = "## END OFFLINE.Speedy - ${section}"; 72 | 73 | $this->contents .= "\n\n" . implode("\n", $append); 74 | } 75 | 76 | protected function sectionRegex($section) 77 | { 78 | return "/(## START OFFLINE\.Speedy - ${section}.*## END OFFLINE\.Speedy - ${section})/ims"; 79 | } 80 | 81 | } -------------------------------------------------------------------------------- /Plugin.php: -------------------------------------------------------------------------------- 1 | 'offline.speedy::lang.plugin.name', 23 | 'description' => 'offline.speedy::lang.plugin.description', 24 | 'author' => 'offline.speedy::lang.plugin.author', 25 | 'homepage' => 'https://offline.swiss', 26 | 'icon' => 'icon-flash', 27 | ]; 28 | } 29 | 30 | public function register() 31 | { 32 | $this->registerConsoleCommand('speedy:toggle-caching', Console\ToggleCachingCommand::class); 33 | $this->registerConsoleCommand('speedy:toggle-gzip', Console\ToggleGzipCommand::class); 34 | $this->registerConsoleCommand('speedy:toggle-http-2', Console\ToggleHttp2Command::class); 35 | $this->registerConsoleCommand('speedy:status', Console\StatusCommand::class); 36 | } 37 | 38 | public function boot() 39 | { 40 | $this->app[Kernel::class]->pushMiddleware(Http2Middleware::class); 41 | $this->app[Kernel::class]->pushMiddleware(CDNMiddleware::class); 42 | } 43 | 44 | public function registerPermissions() 45 | { 46 | return [ 47 | 'offline.speedy.manage_settings' => [ 48 | 'tab' => 'offline.speedy::lang.plugin.name', 49 | 'label' => 'offline.speedy::lang.plugin.manage_settings_permission', 50 | ], 51 | ]; 52 | } 53 | 54 | public function registerSettings() 55 | { 56 | return [ 57 | 'settings' => [ 58 | 'label' => 'offline.speedy::lang.plugin.name', 59 | 'description' => 'offline.speedy::lang.plugin.manage_settings', 60 | 'category' => 'system::lang.system.categories.cms', 61 | 'icon' => 'icon-flash', 62 | 'class' => 'Offline\Speedy\Models\Settings', 63 | 'order' => 500, 64 | 'keywords' => 'speedy caching optimization', 65 | 'permissions' => ['offline.speedy.manage_settings'], 66 | ], 67 | ]; 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /lang/en/lang.php: -------------------------------------------------------------------------------- 1 | [ 3 | 'name' => 'Speedy', 4 | 'description' => 'Speed up your website', 5 | 'author' => 'OFFLINE LLC', 6 | 'manage_settings' => 'Manage Speedy settings', 7 | 'manage_settings_permission' => 'Can manage Speedy settings', 8 | ], 9 | 'settings' => [ 10 | 'quick_hacks' => 'Simple fixes', 11 | 'quick_hacks_comment' => 'These simple fixes can be enabled without further configuration of your server', 12 | 'enable_http2' => 'Enable HTTP/2 preloading', 13 | 'enable_http2_comment' => 'Link headers to preload images, CSS and JS files are generated for every request. Requires an HTTP/2 enabled server.', 14 | 'enable_caching' => 'Enable caching', 15 | 'enable_caching_comment' => 'Adds expires headers for images, fonts, CSS and JS files. (Caution: Enable this setting only in production environments to prevent caching issues while developing)', 16 | 'enable_domain_sharding' => 'Enable domain sharding', 17 | 'enable_domain_sharding_comment' => 'The base URL for all links to your theme folder will be rewritten to another URL.', 18 | 'enable_domain_sharding_in_debug' => 'Enable sharding while debug mode is enabled', 19 | 'enable_domain_sharding_in_debug_comment' => 'By default domain sharding will only be active while the debug mode is turned off.', 20 | 'domain_sharding_cdn_domain' => 'Alternative domain', 21 | 'domain_sharding_cdn_domain_comment' => 'All files are loaded from this URL. Please see the box on the right side for more information.', 22 | 'enable_gzip' => 'Enable Gzip', 23 | 'enable_gzip_comment' => 'Files are compressed using Gzip. Apache mod_gzip has to be installed on your server.', 24 | 'domain_sharding_section' => 'Domain sharding', 25 | 'domain_sharding_section_comment' => 'Load your files from an alternative domain. This option is redundant if you are already using HTTP/2.', 26 | ], 27 | 'sharding' => [ 28 | 'info_heading' => 'Necessary steps to set up domain sharding', 29 | 'info_subheading' => 'Make sure to read this through before enabling the setting!', 30 | 'info_text' => 'Make sure your website is reachable via the alternative domain. You can do this by creating a CNAME record for cdn.example.com that points to www.example.com. Alternatively you can create a subdomain that points to the same root folder as your main domain.', 31 | ], 32 | ]; -------------------------------------------------------------------------------- /lang/pt-pt/lang.php: -------------------------------------------------------------------------------- 1 | [ 3 | 'name' => 'Speedy', 4 | 'description' => 'Melhora a preformance do teu website', 5 | 'author' => 'OFFLINE LLC', 6 | 'manage_settings' => 'Gerenciar configurações do Speedy ', 7 | 'manage_settings_permission' => 'Pode gerir as configurações do Speedy', 8 | ], 9 | 'settings' => [ 10 | 'quick_hacks' => 'Correções simples', 11 | 'quick_hacks_comment' => 'Estas correções simples podem ser ativadas sem nenhuma configuração no servidor.', 12 | 13 | 'enable_http2' => 'Ativar HTTP/2 preloading', 14 | 'enable_http2_comment' => 'Link headers para pré-carregar imagens, arquivos CSS e JS são gerados para cada solicitação. Requer um servidor habilitado para HTTP/2.', 15 | 16 | 'enable_caching' => 'Ativar caching', 17 | 'enable_caching_comment' => 'Adiciona data de validade em imagens, fontes, CSS e arquivos JS. (Cuidado: ative esta configuração somente em ambiente de produção para evitar problemas de cache durante o desenvolvimento)', 18 | 19 | 'enable_domain_sharding' => 'Ativar sharding de domínio', 20 | 'enable_domain_sharding_comment' => 'O URL base de todos os links para a pasta do teu tema serão reescrito com outro URL.', 21 | 22 | 'enable_domain_sharding_in_debug' => 'Ativar sharding enquanto modo debug está ativo', 23 | 'enable_domain_sharding_in_debug_comment' => 'Por defeito sharding de domínio só estará ativo quando debug mode estiver desactivo.', 24 | 25 | 'domain_sharding_cdn_domain' => 'Domínio alternativo', 26 | 'domain_sharding_cdn_domain_comment' => 'Todos os ficheiros serão carregados apartir deste URL. Para mais informação lê a caixa à direita.', 27 | 28 | 'enable_gzip' => 'Ativar Gzip', 29 | 'enable_gzip_comment' => 'Os ficheiros são comprimidos usando Gzip. Tem que ter instalado apache mod_gzip no seu servidor.', 30 | 31 | 'domain_sharding_section' => 'Sharding de domínio', 32 | 'domain_sharding_section_comment' => 'Carrega os teus ficheiros de um domínio alternativo. Esta opção é redundante se estiveres a usar HTTP/2.', 33 | ], 34 | 'sharding' => [ 35 | 'info_heading' => 'Passo necessário para configurar sharding de domínio', 36 | 'info_subheading' => 'Certifica-te que leste isto antes de ativar a configuração!', 37 | 'info_text' => 'Certifica-te que teu website está acessível por um domínio alternativo. Podes fazer isso criando um registro CNAME para cdn.example.com que aponte para www.example.com. Como alternativa, podes criar um subdomínio que aponte para a mesma pasta raiz do seu domínio principal.', 38 | ], 39 | ]; 40 | -------------------------------------------------------------------------------- /lang/ru/lang.php: -------------------------------------------------------------------------------- 1 | [ 4 | 'name' => 'Speedy', 5 | 'description' => 'Ускорение вашего сайта', 6 | 'author' => 'OFFLINE LLC', 7 | 'manage_settings' => 'Управление настройками Speedy', 8 | 'manage_settings_permission' => 'Может управлять настройками Speedy', 9 | ], 10 | 'settings' => [ 11 | 'quick_hacks' => 'Простые ускорители', 12 | 'quick_hacks_comment' => 'Эти простые ускорители могут быть включены без дополнительной настройки вашего сервера', 13 | 'enable_http2' => 'Включить HTTP/2 загрузку', 14 | 'enable_http2_comment' => 'Ссылки заголовков на загрузку изображений, CSS и JS файлов будут сгенерированы для каждого запроса. Требуют настроенного HTTP/2 на сервере.', 15 | 'enable_caching' => 'Включить кеширование', 16 | 'enable_caching_comment' => 'Добавляет времянные зоголовки для изображений, шрифтов, CSS и JS файлов. (Осторожно: Включайте эту настройку только в продакшен, для предотвращения проблем кеширования при разработки)', 17 | 'enable_domain_sharding' => 'Включить доменный шардинг', 18 | 'enable_domain_sharding_comment' => 'Базовый URL для всех ссылок в Вашей теме будет переписан на другой URL.', 19 | 'enable_domain_sharding_in_debug' => 'Включить доменный шардинг только в режиме отладки сайта', 20 | 'enable_domain_sharding_in_debug_comment' => 'Доменный шардинг будет включен только в режиме отладки сайта (debug => true)', 21 | 'domain_sharding_cdn_domain' => 'Альтернативный домен', 22 | 'domain_sharding_cdn_domain_comment' => 'Все файлы будут загружены с этого URL. Посмотрите на поле с правой стороны для получения дополнительной информации.', 23 | 'enable_gzip' => 'Включить Gzip', 24 | 'enable_gzip_comment' => 'Файлы будут сжаты с использованием Gzip. Apache mod_gzip должен быть установлен на ваш сервер.', 25 | 'domain_sharding_section' => 'Доменный шардинг', 26 | 'domain_sharding_section_comment' => 'Загрузка ваших файлов с альтернативного домена. Это опция не обязятельна при использовании HTTP/2.', 27 | ], 28 | 'sharding' => [ 29 | 'info_heading' => 'Необходимые шаги настройки доменного шардинга', 30 | 'info_subheading' => 'Убедитесь, что прочитали это прежде, чем применить настройки!', 31 | 'info_text' => 'Убедитесь, что Ваш веб-сайт доступен через альтернативный домен. Вы можете создать запись CNAME для cdn.example.com, который указывает на www.example.com. Или Вы можете создать субдомен, который указывает на ту же корневую папку как Ваш основной домен.', 32 | ], 33 | ]; -------------------------------------------------------------------------------- /lang/de/lang.php: -------------------------------------------------------------------------------- 1 | [ 3 | 'name' => 'Speedy', 4 | 'description' => 'Beschleunige Deine Website', 5 | 'author' => 'OFFLINE GmbH', 6 | 'manage_settings' => 'Speedy-Einstellungen verwalten', 7 | 'manage_settings_permission' => 'Darf Speedy-Einstellungen verwalten', 8 | ], 9 | 'settings' => [ 10 | 'quick_hacks' => 'Einfache Korrekturen', 11 | 'quick_hacks_comment' => 'Diese Korrekturen benötigen keine weiteren Anpassungen an deinem Server.', 12 | 'enable_http2' => 'Aktiviere HTTP/2-Preloading', 13 | 'enable_http2_comment' => 'Link-Header mit Preload-Anweisungen für Bilder, CSS- und JS-Dateien werden für jeden Seitenaufruf generiert. Dein Server muss das HTTP/2-Protokoll unterstützen.', 14 | 'enable_caching' => 'Aktiviere Caching', 15 | 'enable_caching_comment' => 'Fügt Expires HTTP-Header für Bilder, Schriften und CSS-/JS-Dateien hinzu (Achtung: Verwende diese Einstellung erst in der Produktivumgebung um Caching-Probleme beim Entwickeln zu vermeiden)', 16 | 'enable_domain_sharding' => 'Aktiviere Domain sharding', 17 | 'enable_domain_sharding_comment' => 'Die Basis-URL für alle Links in deinen Theme-Ordner werden auf eine andere URL umgeschrieben.', 18 | 'enable_domain_sharding_in_debug' => 'Sharding auch bei eingeschaltetem Debug-Modus aktivieren', 19 | 'enable_domain_sharding_in_debug_comment' => 'Standardmässig wird das Domain sharding nur aktiv, wenn du den Debug-Modus ausschaltest.', 20 | 'domain_sharding_cdn_domain' => 'Alternative Domain', 21 | 'domain_sharding_cdn_domain_comment' => 'Alle Dateien werden von dieser URL geladen. Beachte die Infobox auf der rechten Seite.', 22 | 'enable_gzip' => 'Aktiviere Gzip', 23 | 'enable_gzip_comment' => 'Dateien werden mit Gzip komprimiert bevor sie an den Client gesendet werden. Apache mod_gzip wird benötigt.', 24 | 'domain_sharding_section' => 'Domain sharding', 25 | 'domain_sharding_section_comment' => 'Lade deine Website-Daten von einer weiteren Domain. Diese Option ist überflüssig, wenn du bereits HTTP/2 verwendest.', 26 | ], 27 | 'sharding' => [ 28 | 'info_heading' => 'Nötige Schritte um das Domain sharding zu verwenden', 29 | 'info_subheading' => 'Führe diese Schritte aus, bevor du die Option aktivierst!', 30 | 'info_text' => 'Stelle sicher, dass deine Website unter der alternativen Domain erreichbar ist. Dies kannst du z. B. tun, in dem du einen CNAME-Eintrag für cdn.deinewebsite.com mit dem Ziel www.deinewebsite.com erstellst. Alternativ kannst du bei deinem Hoster auch eine Subdomain auf den gleichen Zeilordner einrichten.', 31 | ], 32 | ]; -------------------------------------------------------------------------------- /lang/fr/lang.php: -------------------------------------------------------------------------------- 1 | [ 3 | 'name' => 'Speedy', 4 | 'description' => 'Accélérez votre site web', 5 | 'author' => 'OFFLINE LLC', 6 | 'manage_settings' => 'Gérer les réglages de Speedy', 7 | 'manage_settings_permission' => 'Peut gérer les réglages de Speedy', 8 | ], 9 | 'settings' => [ 10 | 'quick_hacks' => 'Améliorations simples', 11 | 'quick_hacks_comment' => 'Ces améliorations simples peuvent être activées sans configuration supplémentaire de votre serveur.', 12 | 'enable_http2' => 'Activer le préchargement HTTP/2', 13 | 'enable_http2_comment' => 'Des en-têtes de lien pour précharger les images, les fichiers CSS et JS sont générés pour chaque requête. Nécessite un serveur compatible HTTP/2.', 14 | 'enable_caching' => 'Activer la mise en cache', 15 | 'enable_caching_comment' => 'Ajoute des en-têtes d\'expiration pour les images, les polices, les fichiers CSS et JS. (Attention : Activez ce paramètre uniquement dans les environnements de production pour éviter les problèmes de mise en cache lors du développement.)', 16 | 'enable_domain_sharding' => 'Activer le "sharding" de domaine', 17 | 'enable_domain_sharding_comment' => 'L\'URL de base de tous les liens vers votre dossier de thème sera réécrit avec une autre URL.', 18 | 'enable_domain_sharding_in_debug' => 'Activer le "sharding" quand le mode debug est activé.', 19 | 'enable_domain_sharding_in_debug_comment' => 'Par défaut, le "sharding" de domaine ne sera actif que lorsque le mode débogage est désactivé.', 20 | 'domain_sharding_cdn_domain' => 'Domaine alternatif', 21 | 'domain_sharding_cdn_domain_comment' => 'Tous les fichiers sont chargés à partir de cette URL. Veuillez consulter la boîte à droite pour plus d\'informations.', 22 | 'enable_gzip' => 'Activer Gzip', 23 | 'enable_gzip_comment' => 'Les fichiers sont compressés à l\'aide de Gzip. Apache mod_gzip doit être installé sur votre serveur.', 24 | 'domain_sharding_section' => '"Sharding" de domaine', 25 | 'domain_sharding_section_comment' => 'Chargez vos fichiers à partir d\'un domaine alternatif. Cette option est redondante si vous utilisez déjà HTTP/2.', 26 | ], 27 | 'sharding' => [ 28 | 'info_heading' => 'Etapes nécessaires à la mise en place du "sharding" de domaine', 29 | 'info_subheading' => 'Veuillez lire attentivement ces informations avant d\'activer le réglage !', 30 | 'info_text' => 'Assurez-vous que votre site Web est accessible via le domaine alternatif. Vous pouvez le faire en créant un enregistrement CNAME pour cdn.example.com qui pointe vers www.example.com. Vous pouvez également créer un sous-domaine qui pointe vers le même dossier racine que votre domaine principal.', 31 | ], 32 | ]; -------------------------------------------------------------------------------- /classes/middleware/Http2Middleware.php: -------------------------------------------------------------------------------- 1 | shouldHandle($request, $response)) { 40 | return $response; 41 | } 42 | 43 | $this->generateAndAttachLinkHeaders($response); 44 | 45 | return $response; 46 | } 47 | 48 | /** 49 | * @param \Illuminate\Http\Response $response 50 | * 51 | * @return $this 52 | */ 53 | protected function generateAndAttachLinkHeaders(Response $response) 54 | { 55 | $headers = $this->fetchLinkableNodes($response) 56 | ->flatten(1) 57 | ->map(function ($url) { 58 | return $this->buildLinkHeaderString($url); 59 | }) 60 | ->filter() 61 | ->implode(','); 62 | 63 | if ( ! empty(trim($headers))) { 64 | $this->addLinkHeader($response, $headers); 65 | } 66 | 67 | return $this; 68 | } 69 | 70 | /** 71 | * Get the DomCrawler instance. 72 | * 73 | * @param \Illuminate\Http\Response $response 74 | * 75 | * @return \Symfony\Component\DomCrawler\Crawler 76 | */ 77 | protected function getCrawler(Response $response) 78 | { 79 | if ($this->crawler) { 80 | return $this->crawler; 81 | } 82 | 83 | return $this->crawler = new Crawler($response->getContent()); 84 | } 85 | 86 | /** 87 | * Get all nodes we are interested in pushing. 88 | * 89 | * @param \Illuminate\Http\Response $response 90 | * 91 | * @return \Illuminate\Support\Collection 92 | */ 93 | protected function fetchLinkableNodes($response) 94 | { 95 | $crawler = $this->getCrawler($response); 96 | 97 | return collect($crawler->filter('link, script[src], img[src]')->extract(['src', 'href'])); 98 | } 99 | 100 | /** 101 | * Build out header string based on asset extension. 102 | * 103 | * @param string $url 104 | * 105 | * @return string 106 | */ 107 | private function buildLinkHeaderString($url) 108 | { 109 | $linkTypeMap = [ 110 | '.CSS' => 'style', 111 | '.JS' => 'script', 112 | '.BMP' => 'image', 113 | '.GIF' => 'image', 114 | '.JPG' => 'image', 115 | '.JPEG' => 'image', 116 | '.PNG' => 'image', 117 | '.TIFF' => 'image', 118 | ]; 119 | $type = collect($linkTypeMap)->first(function ($extension) use ($url) { 120 | return str_contains(strtoupper($url), $extension); 121 | }); 122 | 123 | return is_null($type) ? null : "<{$url}>; rel=preload; as={$type}"; 124 | } 125 | 126 | /** 127 | * Add Link Header 128 | * 129 | * @param \Illuminate\Http\Response $response 130 | * 131 | * @param $link 132 | */ 133 | private function addLinkHeader(Response $response, $link) 134 | { 135 | $response->header('Link', $link); 136 | } 137 | 138 | /** 139 | * @param Request $request 140 | * @param $response 141 | * 142 | * @return bool 143 | */ 144 | protected function shouldHandle(Request $request, $response) 145 | { 146 | return 147 | (bool)Settings::get('enable_http2', true) === true 148 | && $response instanceof Response 149 | && ! $response->isRedirection() 150 | && ! $request->isJson(); 151 | } 152 | } --------------------------------------------------------------------------------