PluginProbe
Powered Cache – Caching and Optimization for WordPress – Easily Improve PageSpeed & Web Vitals Score / trunk
Powered Cache – Caching and Optimization for WordPress – Easily Improve PageSpeed & Web Vitals Score vtrunk
trunk 1.0 1.0.1 1.1 1.1.1 1.1.2 1.2 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 2.0 2.0.1 2.0.2 2.0.3 2.0.4 2.1 2.1.1 2.1.2 2.2 2.2.1 All 69 releases
powered-cache / includes / classes / Htaccess.php

Htaccess.php in Powered Cache – Caching and Optimization for WordPress – Easily Improve PageSpeed & Web Vitals Score trunk, at includes/classes/Htaccess.php

599 lines 19.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Htaccress rules
4 *
5 * @package PoweredCache
6 */
7
8 namespace PoweredCache;
9
10 use function PoweredCache\Utils\get_cache_dir;
11 use function PoweredCache\Utils\mobile_browsers;
12 use function PoweredCache\Utils\mobile_prefixes;
13 use function PoweredCache\Utils\permalink_structure_has_trailingslash;
14
15 if ( ! defined( 'ABSPATH' ) ) {
16 exit; // Exit if accessed directly.
17 }
18
19 // phpcs:disable Generic.Strings.UnnecessaryStringConcat.Found
20
21 /**
22 * Class Htaccess
23 */
24 class Htaccess {
25 /**
26 * Plugin settings
27 *
28 * @var $settings
29 */
30 private $settings;
31
32
33 /**
34 * placeholder
35 *
36 * @since 2.5
37 */
38 public function __construct() {
39 $this->settings = \PoweredCache\Utils\get_settings();
40 }
41
42 /**
43 * Return an instance of the current class
44 *
45 * @return Htaccess
46 */
47 public static function factory() {
48 static $instance = false;
49
50 if ( ! $instance ) {
51 $instance = new self();
52 }
53
54 return $instance;
55 }
56
57 /**
58 * Get htaccess rules
59 *
60 * @return string
61 */
62 public function htaccess_rules() {
63 $rules = '';
64 /**
65 * Filters base htaccess rules
66 *
67 * @hook powered_cache_pre_htaccess
68 *
69 * @param {string} empty htaccesss rules by default
70 *
71 * @return {boolean} New value.
72 *
73 * @since 1.1
74 */
75 $rules .= apply_filters( 'powered_cache_pre_htaccess', '' );
76
77 $rules .= '# BEGIN POWERED CACHE' . PHP_EOL;
78
79 $rules .= $this->browser_cache_rules();
80 $rules .= $this->cors_rules();
81 $rules .= $this->gzip_rules();
82 $rules .= $this->etag_rules();
83 $rules .= $this->cache_control_rules();
84 $rules .= $this->file_optimizer_rewrite_rules();
85 $rules .= $this->rewrite_rules();
86
87 /**
88 * Filters post htaccess rules
89 *
90 * @hook powered_cache_after_htaccess
91 *
92 * @param {string} empty htaccesss rules by default
93 *
94 * @return {boolean} New value.
95 *
96 * @since 2.0
97 */
98 $rules .= apply_filters( 'powered_cache_after_htaccess', '' );
99
100 $rules .= '# END POWERED CACHE' . PHP_EOL;
101
102 return $rules;
103 }
104
105
106 /**
107 * Browser cache rules
108 *
109 * @return string
110 */
111 public function browser_cache_rules() {
112 $rules = '';
113 /**
114 * Filters whether doing the configuration for browser cache or not
115 *
116 * @hook powered_cache_browser_cache
117 *
118 * @param {boolean} true for creating .htaccess rules for browser cache
119 *
120 * @return {boolean} New value.
121 *
122 * @since 1.1
123 */
124 if ( apply_filters( 'powered_cache_browser_cache', true ) ) {
125
126 $mime_types = [
127 'text/css',
128 'application/javascript',
129 'image/jpeg',
130 'image/gif',
131 'image/png',
132 'image/bmp',
133 'image/tiff',
134 'image/webp',
135 'image/heic',
136 'image/svg+xml',
137 'font/ttf',
138 'font/woff',
139 'font/woff2',
140 'font/otf',
141 'application/vnd.ms-fontobject',
142 'image/x-icon',
143 'application/atom+xml',
144 'application/rss+xml',
145 ];
146
147 // set expire time
148 $rules .= '<IfModule mod_expires.c>' . PHP_EOL;
149 $rules .= ' ExpiresActive On' . PHP_EOL;
150 $rules .= ' ExpiresByType text/html "access plus 0 seconds"' . PHP_EOL;
151 $rules .= ' ExpiresByType text/richtext "access plus 0 seconds"' . PHP_EOL;
152 $rules .= ' ExpiresByType text/plain "access plus 0 seconds"' . PHP_EOL;
153 $rules .= ' ExpiresByType text/xsd "access plus 0 seconds"' . PHP_EOL;
154 $rules .= ' ExpiresByType text/xsl "access plus 0 seconds"' . PHP_EOL;
155 $rules .= ' ExpiresByType text/xml "access plus 0 seconds"' . PHP_EOL;
156 $rules .= ' ExpiresByType application/xml "access plus 0 seconds"' . PHP_EOL;
157 $rules .= ' ExpiresByType application/json "access plus 0 seconds"' . PHP_EOL;
158 $rules .= ' ExpiresByType text/cache-manifest "access plus 0 seconds"' . PHP_EOL;
159
160 foreach ( $mime_types as $mime_type ) {
161 $rules .= sprintf( ' ExpiresByType %s "%s"', str_pad( $mime_type, 30, ' ' ), $this->get_browser_cache_lifespan( $mime_type ) ) . PHP_EOL;
162 }
163
164 $rules .= '</IfModule>' . PHP_EOL;
165 }
166
167 return $rules;
168 }
169
170 /**
171 * Cors rules - when CDN integration activated
172 *
173 * @return string
174 */
175 public function cors_rules() {
176 $rules = '';
177 /**
178 * Filters whether add CORS configuration or not
179 *
180 * @hook powered_cache_htaccess_add_cors
181 *
182 * @param {boolean} true for creating .htaccess rules for CORS
183 *
184 * @return {boolean} New value.
185 *
186 * @since 2.5
187 */
188 if ( apply_filters( 'powered_cache_htaccess_add_cors', $this->settings['enable_cdn'] ) ) {
189 /**
190 * Add CORS configuration
191 *
192 * @link https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image
193 * @since 2.1
194 */
195 $rules .= '<IfModule mod_setenvif.c>' . PHP_EOL;
196 $rules .= ' <IfModule mod_headers.c>' . PHP_EOL;
197 $rules .= ' <FilesMatch "\.(avifs?|bmp|cur|gif|ico|jpe?g|jxl|a?png|svgz?|webp)$">' . PHP_EOL;
198 $rules .= ' SetEnvIf Origin ":" IS_CORS' . PHP_EOL;
199 $rules .= ' Header set Access-Control-Allow-Origin "*" env=IS_CORS' . PHP_EOL;
200 $rules .= ' </FilesMatch>' . PHP_EOL;
201 $rules .= ' </IfModule>' . PHP_EOL;
202 $rules .= '</IfModule>' . PHP_EOL . PHP_EOL;
203
204 // configure fonts
205 $rules .= '<FilesMatch "\.(ttf|ttc|otf|eot|woff|woff2|font.css)$">' . PHP_EOL;
206 $rules .= ' <IfModule mod_headers.c>' . PHP_EOL;
207 $rules .= ' Header set Access-Control-Allow-Origin "*"' . PHP_EOL;
208 $rules .= ' </IfModule>' . PHP_EOL;
209 $rules .= '</FilesMatch>' . PHP_EOL . PHP_EOL;
210 }
211
212 return $rules;
213 }
214
215 /**
216 * Gzip rules
217 *
218 * @return string
219 */
220 public function gzip_rules() {
221 $rules = '';
222
223 $rules .= '<IfModule filter_module>' . PHP_EOL;
224 $rules .= ' <IfModule version.c>' . PHP_EOL;
225 $rules .= ' <IfVersion >= 2.4>' . PHP_EOL;
226 $rules .= ' FilterDeclare COMPRESS' . PHP_EOL;
227 $rules .= ' FilterProvider COMPRESS DEFLATE "%{CONTENT_TYPE} = \'application/atom+xml\'"' . PHP_EOL;
228 $rules .= ' FilterProvider COMPRESS DEFLATE "%{CONTENT_TYPE} = \'application/javascript\'"' . PHP_EOL;
229 $rules .= ' FilterProvider COMPRESS DEFLATE "%{CONTENT_TYPE} = \'application/json\'"' . PHP_EOL;
230 $rules .= ' FilterProvider COMPRESS DEFLATE "%{CONTENT_TYPE} = \'application/ld+json\'"' . PHP_EOL;
231 $rules .= ' FilterProvider COMPRESS DEFLATE "%{CONTENT_TYPE} = \'application/manifest+json\'"' . PHP_EOL;
232 $rules .= ' FilterProvider COMPRESS DEFLATE "%{CONTENT_TYPE} = \'application/rss+xml\'"' . PHP_EOL;
233 $rules .= ' FilterProvider COMPRESS DEFLATE "%{CONTENT_TYPE} = \'application/vnd.ms-fontobject\'"' . PHP_EOL;
234 $rules .= ' FilterProvider COMPRESS DEFLATE "%{CONTENT_TYPE} = \'application/xhtml+xml\'"' . PHP_EOL;
235 $rules .= ' FilterProvider COMPRESS DEFLATE "%{CONTENT_TYPE} = \'application/xml\'"' . PHP_EOL;
236 $rules .= ' FilterProvider COMPRESS DEFLATE "%{CONTENT_TYPE} = \'font/opentype\'"' . PHP_EOL;
237 $rules .= ' FilterProvider COMPRESS DEFLATE "%{CONTENT_TYPE} = \'image/svg+xml\'"' . PHP_EOL;
238 $rules .= ' FilterProvider COMPRESS DEFLATE "%{CONTENT_TYPE} = \'image/x-icon\'"' . PHP_EOL;
239 $rules .= ' FilterProvider COMPRESS DEFLATE "%{CONTENT_TYPE} = \'text/html\'"' . PHP_EOL;
240 $rules .= ' FilterProvider COMPRESS DEFLATE "%{CONTENT_TYPE} = \'text/plain\'"' . PHP_EOL;
241 $rules .= ' FilterProvider COMPRESS DEFLATE "%{CONTENT_TYPE} = \'text/x-component\'"' . PHP_EOL;
242 $rules .= ' FilterProvider COMPRESS DEFLATE "%{CONTENT_TYPE} = \'text/xml\'"' . PHP_EOL;
243 $rules .= ' FilterChain COMPRESS' . PHP_EOL;
244 $rules .= ' FilterProtocol COMPRESS DEFLATE change=yes;byteranges=no' . PHP_EOL;
245 $rules .= ' </IfVersion>' . PHP_EOL;
246 $rules .= ' </IfModule>' . PHP_EOL;
247 $rules .= '</IfModule>' . PHP_EOL;
248
249 $rules .= '<IfModule mod_deflate.c>' . PHP_EOL;
250 $rules .= ' SetOutputFilter DEFLATE' . PHP_EOL;
251 $rules .= ' <IfModule mod_setenvif.c>' . PHP_EOL;
252 $rules .= ' <IfModule mod_headers.c>' . PHP_EOL;
253 $rules .= ' SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding' . PHP_EOL;
254 $rules .= ' RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding' . PHP_EOL;
255 $rules .= ' </IfModule>' . PHP_EOL;
256 $rules .= ' </IfModule>' . PHP_EOL;
257 $rules .= ' <IfModule mod_filter.c>' . PHP_EOL;
258 $rules .= ' AddOutputFilterByType DEFLATE application/atom+xml \
259 application/javascript \
260 application/json \
261 application/ld+json \
262 application/manifest+json \
263 application/rss+xml \
264 application/vnd.ms-fontobject \
265 application/x-font-ttf \
266 application/xhtml+xml \
267 application/xml \
268 font/opentype \
269 image/svg+xml \
270 image/x-icon \
271 text/html \
272 text/plain \
273 text/css \
274 text/x-component \
275 text/xml' . PHP_EOL;
276 $rules .= ' </IfModule>' . PHP_EOL;
277 $rules .= ' <IfModule mod_headers.c>' . PHP_EOL;
278 $rules .= ' Header append Vary: Accept-Encoding' . PHP_EOL;
279 $rules .= ' </IfModule>' . PHP_EOL;
280 $rules .= '</IfModule>' . PHP_EOL . PHP_EOL;
281
282 return $rules;
283 }
284
285 /**
286 * Remove ETag rules
287 *
288 * @link https://htaccessbook.com/disable-etags/
289 * @return string
290 */
291 public function etag_rules() {
292 // remove etag
293 $rules = '# Remove ETag' . PHP_EOL;
294 $rules = '<IfModule mod_headers.c>' . PHP_EOL;
295 $rules .= 'Header unset ETag' . PHP_EOL;
296 $rules .= '</IfModule>' . PHP_EOL;
297 $rules .= 'FileETag None' . PHP_EOL . PHP_EOL;
298
299 return $rules;
300 }
301
302 /**
303 * Rewrite rules
304 *
305 * @return string
306 */
307 public function rewrite_rules() {
308 $rules = '';
309 /**
310 * Filters whether doing the configuration for htaccess rewrite cache or not
311 *
312 * @hook powered_cache_mod_rewrite
313 *
314 * @param {boolean} true for creating .htaccess rewrite rules.
315 *
316 * @return {boolean} New value.
317 *
318 * @since 2.0
319 */
320 if ( apply_filters( 'powered_cache_mod_rewrite', true ) ) { // rewrite
321 // add gzip type for .html.gz format
322 if ( $this->is_gzip_enabled() ) {
323 $rules .= '<IfModule mod_mime.c>' . PHP_EOL;
324 $rules .= ' AddType text/html .html.gz' . PHP_EOL;
325 $rules .= ' AddEncoding gzip .gz' . PHP_EOL;
326 $rules .= '</IfModule>' . PHP_EOL;
327 $rules .= '<IfModule mod_setenvif.c>' . PHP_EOL;
328 $rules .= ' SetEnvIfNoCase Request_URI \.html.gz$ no-gzip' . PHP_EOL;
329 $rules .= '</IfModule>' . PHP_EOL;
330 }
331
332 $env_powered_cache_ua = '';
333 $env_powered_cache_ssl = '';
334 $env_powered_cache_enc = '';
335
336 $rewrite_base = wp_parse_url( home_url() );
337 $rewrite_base = isset( $rewrite_base['path'] ) ? trailingslashit( $rewrite_base['path'] ) : '/';
338
339 $rules .= '<IfModule mod_rewrite.c>' . PHP_EOL;
340 $rules .= ' RewriteEngine On' . PHP_EOL;
341 $rules .= ' RewriteBase ' . $rewrite_base . PHP_EOL;
342 $rules .= ' AddDefaultCharset UTF-8 ' . PHP_EOL;
343
344 if ( true === $this->settings['cache_mobile'] && true === $this->settings['cache_mobile_separate_file'] ) {
345 $mobile_browsers = addcslashes( implode( '|', preg_split( '/[\s*,\s*]*,+[\s*,\s*]*/', mobile_browsers() ) ), ' ' );
346 $mobile_prefixes = addcslashes( implode( '|', preg_split( '/[\s*,\s*]*,+[\s*,\s*]*/', mobile_prefixes() ) ), ' ' );
347 // mobile env set
348 $rules .= ' RewriteCond %{HTTP_USER_AGENT} (' . $mobile_browsers . ') [NC]' . PHP_EOL;
349 $rules .= ' RewriteRule .* - [E=PC_UA:-mobile]' . PHP_EOL;
350 $rules .= ' RewriteCond %{HTTP_USER_AGENT} ^(' . $mobile_prefixes . ') [NC]' . PHP_EOL;
351 $rules .= ' RewriteRule .* - [E=PC_UA:-mobile]' . PHP_EOL;
352 $env_powered_cache_ua = '%{ENV:PC_UA}';
353 }
354
355 $rules .= ' RewriteCond %{HTTPS} on [OR]' . PHP_EOL;
356 $rules .= ' RewriteCond %{SERVER_PORT} ^443$ [OR]' . PHP_EOL;
357 $rules .= ' RewriteCond %{HTTP:X-Forwarded-Proto} https' . PHP_EOL;
358 $rules .= ' RewriteRule .* - [E=PC_SSL:-https]' . PHP_EOL;
359 $env_powered_cache_ssl = '%{ENV:PC_SSL}';
360
361 if ( $this->is_gzip_enabled() ) {
362 $rules .= ' RewriteCond %{HTTP:Accept-Encoding} gzip' . PHP_EOL;
363 $rules .= ' RewriteRule .* - [E=PC_ENC:.gz]' . PHP_EOL;
364 $env_powered_cache_enc = '%{ENV:PC_ENC}';
365 }
366
367 $rules .= ' RewriteCond %{REQUEST_METHOD} !=POST' . PHP_EOL;
368 $rules .= ' RewriteCond %{QUERY_STRING} =""' . PHP_EOL;
369
370 if ( permalink_structure_has_trailingslash() ) {
371 $rules .= ' RewriteCond %{REQUEST_URI} !^.*[^/]$' . PHP_EOL;
372 $rules .= ' RewriteCond %{REQUEST_URI} !^.*//.*$' . PHP_EOL;
373 }
374
375 // Get root base
376 $site_root = wp_parse_url( site_url() );
377 $site_root = isset( $site_root['path'] ) ? trailingslashit( $site_root['path'] ) : '';
378
379 // reject user agent
380 $rejected_user_agents = (array) AdvancedCache::get_rejected_user_agents();
381 if ( ! empty( $rejected_user_agents ) ) {
382 $rules .= ' RewriteCond %{HTTP_USER_AGENT} !^(' . implode( '|', $rejected_user_agents ) . ').* [NC]' . PHP_EOL;
383 }
384
385 // rejected cookies
386 $rejected_cookies = AdvancedCache::get_rejected_cookies();
387 if ( ! empty( $rejected_cookies ) ) {
388 $rules .= ' RewriteCond %{HTTP:Cookie} !(' . implode( '|', $rejected_cookies ) . ') [NC]' . PHP_EOL;
389 }
390
391 // dont cache fbexternal
392 $rules .= ' RewriteCond %{HTTP_USER_AGENT} !^(facebookexternalhit).* [NC]' . PHP_EOL;
393
394 $cache_location = get_cache_dir();
395 $cache_location = untrailingslashit( $cache_location ) . '/powered-cache/';
396 if ( strpos( ABSPATH, $cache_location ) === false ) {
397 $cache_path = str_replace( $_SERVER['DOCUMENT_ROOT'], '', $cache_location ); // phpcs:ignore
398 } else {
399 $cache_path = $site_root . str_replace( ABSPATH, '', $cache_location );
400 }
401
402 /**
403 * Filters whether running on 1and1_hosting or not
404 *
405 * @hook powered_cache_maybe_1and1_hosting
406 *
407 * @param {boolean} $status true if /kunden/homepage directory exists
408 *
409 * @return {boolean} New value.
410 *
411 * @since 1.1
412 */
413 if ( apply_filters( 'powered_cache_maybe_1and1_hosting', ( 0 === strpos( $_SERVER['DOCUMENT_ROOT'], '/kunden/homepage/' ) ) ) ) { // phpcs:ignore
414 $rules .= ' RewriteCond "' . str_replace( '/kunden/homepage/', '/', $cache_location ) . '%{HTTP_HOST}' . '%{REQUEST_URI}/index' . $env_powered_cache_ssl . $env_powered_cache_ua . '.html' . $env_powered_cache_enc . '" -f' . PHP_EOL;
415 } else {
416 $rules .= ' RewriteCond "%{DOCUMENT_ROOT}/' . ltrim( $cache_path, '/' ) . '%{HTTP_HOST}' . '%{REQUEST_URI}/index' . $env_powered_cache_ssl . $env_powered_cache_ua . '.html' . $env_powered_cache_enc . '" -f' . PHP_EOL;
417 }
418 $rules .= ' RewriteRule .* "' . $cache_path . '%{HTTP_HOST}' . '%{REQUEST_URI}/index' . $env_powered_cache_ssl . $env_powered_cache_ua . '.html' . $env_powered_cache_enc . '" [L]' . PHP_EOL;
419
420 if ( $this->is_gzip_enabled() ) {
421 $rules .= ' # prevent mod_deflate double gzip' . PHP_EOL;
422 $rules .= ' RewriteRule \.html\.gz$ - [T=text/html,E=no-gzip:1]' . PHP_EOL;
423 }
424
425 $rules .= '</IfModule>' . PHP_EOL;
426 }
427
428 return $rules;
429 }
430
431 /**
432 * Apache allows both format like A2592000 => "access plus 1 month"
433 * A => access, M => Modified
434 *
435 * @param string $mime_type Mimetype
436 *
437 * @return string cache lifespan for apache
438 * @see http://httpd.apache.org/docs/current/mod/mod_expires.html
439 * @since 2.0
440 */
441 public function get_browser_cache_lifespan( $mime_type ) {
442 switch ( $mime_type ) {
443 case 'text/css':
444 case 'application/javascript':
445 /**
446 * Filters TTL for CSS/JS files.
447 *
448 * @hook powered_cache_browser_cache_assets_lifespan
449 *
450 * @param {string} $expiry_time .htaccess lifespan
451 *
452 * @return {string} New value.
453 *
454 * @since 1.1
455 */
456 $expiry_time = apply_filters( 'powered_cache_browser_cache_assets_lifespan', 'access plus 1 year' );
457 break;
458 case 'image/jpeg':
459 case 'image/gif':
460 case 'image/png':
461 case 'image/bmp':
462 case 'image/tiff':
463 case 'image/webp':
464 case 'image/heic':
465 $expiry_time = 'access plus 6 months';
466 break;
467 case 'image/x-icon':
468 $expiry_time = 'access plus 1 week'; // favicon
469 break;
470 case 'image/svg+xml':
471 case 'font/ttf':
472 case 'font/woff':
473 case 'font/woff2':
474 case 'font/otf':
475 case 'application/vnd.ms-fontobject':
476 $expiry_time = 'access plus 4 month';
477 break;
478 case 'application/atom+xml':
479 case 'application/rss+xml':
480 $expiry_time = 'access plus 1 hour';
481 break;
482 default:
483 /**
484 * Filters default TTL for browser cache lifespan.
485 *
486 * @hook powered_cache_browser_cache_default_lifespan
487 *
488 * @param {string} $expiry_time .htaccess lifespan
489 *
490 * @return {string} New value.
491 *
492 * @since 1.1
493 */
494 $expiry_time = apply_filters( 'powered_cache_browser_cache_default_lifespan', 'access plus 1 month' );
495 }
496
497 /**
498 * Filters TTL for browser cache lifespan.
499 *
500 * @hook powered_cache_browser_cache_lifespan
501 *
502 * @param {string} $expiry_time .htaccess lifespan
503 * @param {string} $mime_type mime type
504 *
505 * @return {string} New value.
506 *
507 * @since 2.0
508 */
509 $expiry_time = apply_filters( 'powered_cache_browser_cache_lifespan', $expiry_time, $mime_type );
510
511 return $expiry_time;
512 }
513
514
515 /**
516 * Check if the gzip option enabled
517 *
518 * @return bool
519 * @since 2.5
520 */
521 public function is_gzip_enabled() {
522 /**
523 * Filters whether gzip enabled or not for the htaccess rules
524 *
525 * @hook powered_cache_htaccess_enable_gzip_compression
526 *
527 * @param {boolean} $status true if gzip option enabled on settings page
528 *
529 * @return {boolean} New value.
530 *
531 * @since 2.5
532 */
533 return function_exists( 'gzencode' ) && apply_filters( 'powered_cache_htaccess_enable_gzip_compression', $this->settings['gzip_compression'] );
534 }
535
536
537 /**
538 * Cache-Control rules
539 *
540 * @return string
541 */
542 public function cache_control_rules() {
543 $rules = '<FilesMatch "\.(html|htm|html\.gz|rtf|rtx|txt|xsd|xsl|xml)$">' . PHP_EOL;
544 $rules .= ' <IfModule mod_headers.c>' . PHP_EOL;
545 $rules .= ' Header set X-Powered-By "Powered Cache"' . PHP_EOL;
546 $rules .= ' Header unset Pragma' . PHP_EOL;
547 $rules .= ' Header append Cache-Control "public"' . PHP_EOL;
548 $rules .= ' </IfModule>' . PHP_EOL;
549 $rules .= '</FilesMatch>' . PHP_EOL;
550
551 /**
552 * Filters cache control rules
553 *
554 * @hook powered_cache_htaccess_cache_control_rules
555 *
556 * @param {string} $rules cache control
557 *
558 * @return {string} New value.
559 *
560 * @since 2.5
561 */
562 $rules = apply_filters( 'powered_cache_htaccess_cache_control_rules', $rules );
563
564 return $rules;
565 }
566
567
568 /**
569 * Rewrite rules for file optimizer
570 *
571 * @return string|void
572 * @since 3.3
573 * https://example.com/wp-content/plugins/powered-cache/includes/file-optimizer.php??
574 * to
575 * https://example.com/_static/??
576 * @see https://docs.poweredcache.com/rewrite-file-optimizer/
577 */
578 public function file_optimizer_rewrite_rules() {
579 if ( ! $this->settings['rewrite_file_optimizer'] ) {
580 return;
581 }
582
583 $file_optimizer_path = \PoweredCache\Optimizer\Helper::get_file_optimizer_relative_path();
584
585 $rules = '';
586 $rules .= '<IfModule mod_rewrite.c>' . PHP_EOL;
587 $rules .= ' # Enable rewrite engine' . PHP_EOL;
588 $rules .= ' RewriteEngine On' . PHP_EOL;
589 $rules .= ' # Redirect all requests starting with /_static/' . PHP_EOL;
590 $rules .= ' RewriteRule ^_static/.* ' . $file_optimizer_path . ' [L]' . PHP_EOL;
591 $rules .= '</IfModule>' . PHP_EOL;
592
593 return $rules;
594 }
595
596 }
597
598
599