{"id":305698,"date":"2020-06-21T21:00:49","date_gmt":"2020-06-21T21:00:49","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=305698"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=305698","title":{"rendered":"\u0421\u0435\u0440\u0432\u0438\u0440\u043e\u0432\u043a\u0430 \u0441\u0436\u0430\u0442\u044b\u0445 \u0444\u0430\u0439\u043b\u043e\u0432 \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 CDN"},"content":{"rendered":"\n<div class=\"post__text post__text-html post__text_v1\" id=\"post-content-body\" data-io-article-url=\"https:\/\/habr.com\/ru\/post\/507402\/\">\n<p>\u041f\u0440\u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0435 \u0441\u0430\u0439\u0442\u0430 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440 \u043b\u043e\u0436\u0438\u0442\u0441\u044f \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e \u0437\u0430\u0434\u0430\u0447, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u0431\u044b\u0441\u0442\u0440\u043e \u0438 \u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u043e. \u041d\u043e \u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0441\u0442\u044c \u0437\u0430 \u0447\u0430\u0441\u0442\u044c \u0438\u0437 \u043d\u0438\u0445 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043d\u0430 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0444\u0430\u0439\u043b\u043e\u0432, \u0438\u0445 \u043f\u0435\u0440\u0435\u0441\u044b\u043b\u043a\u0443 \u043a\u043b\u0438\u0435\u043d\u0442\u0443 \u0438 \u043a\u043e\u043c\u043f\u0440\u0435\u0441\u0441\u0438\u044e \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0435\u043c\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445) \u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u043b\u043e\u0436\u0438\u0442\u044c \u043d\u0430 \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u044b\u0435 \u0444\u0430\u0439\u043b\u043e\u0432\u044b\u0435 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430. \u041e\u043d\u0438, \u043a\u0430\u043a \u043f\u0440\u0430\u0432\u0438\u043b\u043e, \u0438\u043c\u0435\u044e\u0442 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0442\u0435\u0440\u0440\u0438\u0442\u043e\u0440\u0438\u0430\u043b\u044c\u043d\u044b\u0445 \u0437\u043e\u043d \u0438 \u043e\u0442\u0434\u0430\u044e\u0442 \u043a\u043b\u0438\u0435\u043d\u0442\u0443 \u0444\u0430\u0439\u043b\u044b \u043e\u0442 \u043d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u0431\u043b\u0438\u0437\u043a\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0435\u0440\u0430 (\u0442\u0430\u043a, \u0432\u0440\u0435\u043c\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432 \u0438\u0437 \u0444\u0438\u0437\u0438\u0447\u0435\u0441\u043a\u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u0445 \u043e\u0442 \u0440\u0430\u0441\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u043c\u0435\u0441\u0442 \u0437\u043d\u0430\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0441\u043e\u043a\u0440\u0430\u0442\u0438\u0442\u0441\u044f). \u042d\u0442\u0438 \u043f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f CDN \u2014 \u0440\u0430\u0437\u0433\u0440\u0443\u0437\u043a\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0430, \u0441\u043e\u043a\u0440\u0430\u0449\u0435\u043d\u0438\u0435 \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0434\u043e\u0441\u0442\u0430\u0432\u043a\u0438 \u043a\u043e\u043d\u0442\u0435\u043d\u0442\u0430, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0441\u043e\u043a\u0440\u0430\u0449\u0435\u043d\u0438\u0435 \u0442\u0440\u0430\u0444\u0438\u043a\u0430, \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0435\u043c\u043e\u0433\u043e \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u043c \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c (\u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0431\u044b\u0447\u043d\u043e \u0434\u043e\u0440\u043e\u0436\u0435), \u043f\u0440\u0438\u0432\u0435\u043b\u0438 \u043a \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0448\u0438\u0440\u043e\u043a\u043e\u043c\u0443 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044e \u043f\u043e\u0434\u043e\u0431\u043d\u043e\u0433\u043e \u043f\u043e\u0434\u0445\u043e\u0434\u0430. \u0421\u0435\u0433\u043e\u0434\u043d\u044f \u0440\u0430\u0437\u0431\u0435\u0440\u0435\u043c\u0441\u044f, \u043a\u0430\u043a \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 Amazon S3-\u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u044b\u043c\u0438 CDN \u0438 \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0442\u044c \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0443 \u0441\u0436\u0430\u0442\u044b\u0445 \u0444\u0430\u0439\u043b\u043e\u0432.<\/p>\n<p><a name=\"habracut\"><\/a>  <\/p>\n<h3 id=\"generaciya-szhatyh-faylov\">\u0413\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f \u0441\u0436\u0430\u0442\u044b\u0445 \u0444\u0430\u0439\u043b\u043e\u0432<\/h3>\n<p>  <\/p>\n<p>\u041d\u0430 \u0434\u0430\u043d\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442 2 \u0440\u0430\u0441\u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0435\u043d\u043d\u044b\u0445 \u0432 \u0432\u0435\u0431-\u0441\u0440\u0435\u0434\u0435 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u0430 \u2014 Gzip \u0438 Brotli. \u0412\u0442\u043e\u0440\u043e\u0439 \u043e\u0442\u043b\u0438\u0447\u0430\u0435\u0442\u0441\u044f \u0431\u043e\u043b\u0435\u0435 \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u043e\u0439 \u043a\u043e\u043c\u043f\u0440\u0435\u0441\u0441\u0438\u0435\u0439 (\u043d\u0430 15-20%), \u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e HTTPS (\u0437\u0430 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435\u043c localhost, \u0433\u0434\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0438 \u043f\u043e HTTP) \u0438 \u0432 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0441\u0432\u0435\u0436\u0438\u0445 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430\u0445 (&gt; IE 11). \u041d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 CDN (Amazon CloudFront) \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u044e\u0442 \u0441\u0436\u0430\u0442\u0438\u0435 &quot;\u043d\u0430 \u043b\u0435\u0442\u0443&quot;, \u043d\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 Gzip, \u0432 \u0442\u043e \u0432\u0440\u0435\u043c\u044f \u043a\u0430\u043a \u0434\u0440\u0443\u0433\u0438\u0435 (\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e Yandex Object Storage) \u043b\u0438\u0448\u0435\u043d\u044b \u043f\u043e\u0434\u043e\u0431\u043d\u043e\u0439 \u043b\u043e\u0433\u0438\u043a\u0438, \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u044f \u0441\u043e\u0431\u043e\u0439 \u043f\u0440\u043e\u0441\u0442\u043e \u043e\u0431\u043b\u0430\u0447\u043d\u044b\u0435 \u043f\u0430\u043f\u043a\u0438 \u0441 \u0444\u0430\u0439\u043b\u0430\u043c\u0438 \u0431\u0435\u0437 \u043a\u0430\u043a\u043e\u0439-\u043b\u0438\u0431\u043e \u043b\u043e\u0433\u0438\u043a\u0438, \u043a\u0440\u043e\u043c\u0435 \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u043e\u0439 \u043f\u043e \u0440\u0435\u0433\u0438\u043e\u043d\u0430\u043c \u0434\u043e\u0441\u0442\u0430\u0432\u043a\u0438.<\/p>\n<p>  <\/p>\n<p>\u0421\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u0436\u0430\u0442\u044b\u0435 \u0444\u0430\u0439\u043b\u044b \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u043f\u043b\u0430\u0433\u0438\u043d <a href=\"https:\/\/webpack.js.org\/plugins\/compression-webpack-plugin\/\" rel=\"nofollow\">compression-webpack-plugin<\/a>:<\/p>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">webpack-custom\/plugins\/pluginCompressionGzip.ts<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">import webpack from 'webpack'; import CompressionPlugin from 'compression-webpack-plugin';  export const pluginCompressionGzip: webpack.Plugin = new CompressionPlugin({   test: \/\\.(js|css)$\/i,   cache: true,   filename: '[path].gz',   algorithm: 'gzip',   deleteOriginalAssets: false,   compressionOptions: {     level: 9,   }, });<\/code><\/pre>\n<\/div><\/div>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">webpack-custom\/plugins\/pluginCompressionBrotli.ts<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">import webpack from 'webpack'; import CompressionPlugin from 'compression-webpack-plugin';  export const pluginCompressionBrotli: webpack.Plugin = new CompressionPlugin({   test: \/\\.(js|css)$\/i,   cache: true,   filename: '[path].br',   algorithm: 'brotliCompress',   deleteOriginalAssets: false,   compressionOptions: {     level: 11,   }, });<\/code><\/pre>\n<\/div><\/div>\n<p>  <\/p>\n<p>\u042d\u0442\u0438 \u043f\u043b\u0430\u0433\u0438\u043d\u044b, \u0440\u0430\u0437\u0443\u043c\u0435\u0435\u0442\u0441\u044f, \u043d\u0443\u0436\u043d\u044b \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434\u043e\u0432\u043e\u0439 \u0441\u0431\u043e\u0440\u043a\u0438 (\u043f\u0440\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 \u0438\u0437\u043e\u043c\u043e\u0440\u0444\u043d\u044b\u0445 \u0441\u0431\u043e\u0440\u043e\u043a) \u0438 \u0438\u0445 \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u043e\u0442\u043a\u043b\u044e\u0447\u0430\u0442\u044c \u0434\u043b\u044f \u0443\u0441\u043a\u043e\u0440\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0439 \u0441\u0431\u043e\u0440\u043a\u0438, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e env-\u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439:<\/p>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">webpack-custom\/configs\/configPlugins.ts<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">import webpack from 'webpack';  import { env } from '..\/..\/env'; import { pluginHtml } from '..\/plugins\/pluginHtml'; import { pluginDefine } from '..\/plugins\/pluginDefine'; import { pluginExtract } from '..\/plugins\/pluginExtract'; import { pluginLodashModule } from '..\/plugins\/pluginLodashModule'; import { pluginCompressionGzip } from '..\/plugins\/pluginCompressionGzip'; import { pluginCompressionBrotli } from '..\/plugins\/pluginCompressionBrotli'; import { pluginCircularDependency } from '..\/plugins\/pluginCircularDependency';  export const configPlugins: webpack.Configuration['plugins'] = [   pluginHtml,   pluginDefine,   pluginExtract,   pluginLodashModule,   env.CIRCULAR_CHECK &amp;&amp; pluginCircularDependency,   env.GENERATE_COMPRESSED &amp;&amp; pluginCompressionGzip,   env.GENERATE_COMPRESSED &amp;&amp; pluginCompressionBrotli, ].filter(Boolean);<\/code><\/pre>\n<\/div><\/div>\n<p>  <\/p>\n<h3 id=\"zagruzka-faylov-na-cdn\">\u0417\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u0444\u0430\u0439\u043b\u043e\u0432 \u043d\u0430 CDN<\/h3>\n<p>  <\/p>\n<p>\u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0432\u043e\u043f\u0440\u043e\u0441, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0443\u0436\u043d\u043e \u043e\u0442\u0432\u0435\u0442\u0438\u0442\u044c \u0432 \u044d\u0442\u043e\u043c \u0440\u0430\u0437\u0434\u0435\u043b\u0435 \u2014 \u044d\u0442\u043e \u043d\u0430 \u043a\u0430\u043a\u043e\u043c \u044d\u0442\u0430\u043f\u0435 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0442\u044c \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0444\u0430\u0439\u043b\u044b \u0432 \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u043e\u0435 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435. \u0421\u0445\u0435\u043c\u044b \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0434\u0432\u0435:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u0441\u0440\u0430\u0437\u0443 \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430 \u0441\u0431\u043e\u0440\u043a\u0438. \u0412 \u044d\u0442\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0441\u043e\u0431\u0440\u0430\u043d\u043d\u044b\u0435 \u0430\u0440\u0442\u0435\u0444\u0430\u043a\u0442\u044b \u0434\u0443\u0431\u043b\u0438\u0440\u0443\u044e\u0442\u0441\u044f: \u043e\u0434\u043d\u0430 \u0432\u0435\u0440\u0441\u0438\u044f \u0445\u0440\u0430\u043d\u0438\u0442\u0441\u044f \u0432 CI\/CD (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0432 \u0433\u043e\u0442\u043e\u0432\u043e\u043c \u0434\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0434\u043e\u043a\u0435\u0440-\u043e\u0431\u0440\u0430\u0437\u0435 \u043b\u0438\u0431\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u0432 zip-\u0444\u0430\u0439\u043b\u0435, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u0438 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u0440\u0430\u0441\u043f\u0430\u043a\u043e\u0432\u0430\u0442\u044c \u0438 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435 \u0438\u043b\u0438 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0439 \u043c\u0430\u0448\u0438\u043d\u0435), \u0430 \u0432\u0442\u043e\u0440\u0430\u044f \u043a\u043b\u0430\u0434\u0435\u0442\u0441\u044f \u0432 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0439 bucket (\u0442\u0430\u043a \u043d\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043a\u043e\u0440\u043d\u0435\u0432\u0430\u044f \u043f\u0430\u043f\u043a\u0430 \u0441 \u0444\u0430\u0439\u043b\u0430\u043c\u0438 \u0432 CDN). \u0423\u0434\u043e\u0431\u043d\u043e \u0438\u043c\u0435\u043d\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u0443 \u043f\u0430\u043f\u043a\u0443 \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 <code>projectName_gitCommitHash<\/code>. \u041f\u0440\u0438 \u043f\u0440\u044f\u043c\u043e\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u0435 \u043d\u0430 \u043d\u043e\u0432\u044b\u0439 \u0431\u0430\u043a\u0435\u0442 \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043a\u0435\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 (\u0442.\u043a. \u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f \u0430\u0434\u0440\u0435\u0441 \u0441\u0441\u044b\u043b\u043a\u0438 \u0441 <code>https:\/\/storage\/projectName_333\/client.js<\/code> \u043d\u0430 <code>https:\/\/storage\/projectName_111\/client.js<\/code>), \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0432 \u0434\u0430\u043d\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0440\u0435\u0434\u0438\u0440\u0435\u043a\u0442-\u0441\u0435\u0440\u0432\u0435\u0440. \u041e\u043d \u043f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043d\u0430 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0443\u044e \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u0437\u0430\u043f\u0443\u0449\u0435\u043d\u043d\u043e\u0439 \u0432\u0435\u0440\u0441\u0438\u0438 \u043f\u0430\u043f\u043a\u0443, \u0442\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u044f\u0432\u043b\u044f\u044f\u0441\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u043c \u044d\u0442\u0430\u043f\u043e\u043c \u0432 \u0434\u043e\u0441\u0442\u0430\u0432\u043a\u0435 \u0444\u0430\u0439\u043b\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043e\u0431\u0441\u043b\u0443\u0436\u0438\u0432\u0430\u0442\u044c;<\/li>\n<li>\u0430\u0440\u0442\u0435\u0444\u0430\u043a\u0442\u044b \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u044e\u0442\u0441\u044f \u0432 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 \u0441\u0442\u0435\u043d\u0434\u0443 bucket \u043f\u0440\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0435 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 (\u044d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c <code>projectName_production<\/code> \u0438\u043b\u0438 <code>projectName_some-test-server<\/code>).<\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u0412\u0442\u043e\u0440\u043e\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043d\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043f\u043e\u0434\u0432\u043e\u0434\u043d\u044b\u0445 \u043a\u0430\u043c\u043d\u0435\u0439: \u0435\u0441\u043b\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c contenthash \u0432 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0438 \u0444\u0430\u0439\u043b\u043e\u0432, \u0442\u043e \u043d\u043e\u0432\u044b\u0435 \u0444\u0430\u0439\u043b\u044b \u043d\u0435 \u0431\u0443\u0434\u0443\u0442 \u043f\u0435\u0440\u0435\u0437\u0430\u0442\u0438\u0440\u0430\u0442\u044c \u0441\u0442\u0430\u0440\u044b\u0435, \u0442\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043f\u043e\u0440\u0447\u0438 \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e \u0437\u0430\u043f\u0443\u0449\u0435\u043d\u043d\u044b\u0445 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u0432 \u0441 \u0440\u0430\u0437\u043d\u044b\u043c\u0438 \u0432\u0435\u0440\u0441\u0438\u044f\u043c\u0438; \u043e\u0442\u043a\u0430\u0442\u044b \u043d\u0430 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0438\u0435 \u0432\u0435\u0440\u0441\u0438\u0438 (\u0435\u0441\u043b\u0438 \u0441\u0435\u0440\u0432\u0435\u0440 \u043d\u0435 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u043b\u0441\u044f) \u0431\u0435\u0441\u043f\u0440\u043e\u0431\u043b\u0435\u043c\u043d\u044b; \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e\u0441\u0442\u044c CDN \u0434\u043b\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0444\u0430\u0439\u043b\u043e\u0432 (\u0442\u0430\u043a\u043e\u0435 \u0431\u044b\u0432\u0430\u0435\u0442, \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0438 \u043d\u0435 \u0431\u0435\u0437\u0443\u043f\u0440\u0435\u0447\u043d\u044b) \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u0442 \u043b\u0438\u0448\u044c \u043a \u043e\u0442\u0441\u0440\u043e\u0447\u0438\u0432\u0430\u043d\u0438\u044e \u0440\u0435\u043b\u0438\u0437\u0430, \u043d\u0435 \u043f\u043e\u0432\u043b\u0438\u044f\u0432 \u043d\u0430 \u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0449\u0438\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b.<\/p>\n<p>  <\/p>\n<p>\u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c\u044b\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 (\u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 \u0441\u0431\u043e\u0440\u043a\u0438 \u0432\u0437\u044f\u0442\u0430 <a href=\"https:\/\/habr.com\/ru\/post\/506636\/\">\u043e\u0442\u0441\u044e\u0434\u0430<\/a>):<\/p>\n<p>  <\/p>\n<pre><code class=\"plaintext\">CDN_ENABLED=true CDN_BUCKET=local-bucket CDN_BUCKET_PREFIX=myproject- CDN_ENDPOINT=https:\/\/storage.yandexcloud.net CDN_ACCESS_KEY_ID= CDN_SECRET_ACCESS_KEY=  LOGS_CDN=false<\/code><\/pre>\n<p>  <\/p>\n<p>\u041f\u0440\u0438 \u0432\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u043e\u0439 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0435 <code>CDN_ENABLED<\/code> \u0434\u043b\u044f \u0443\u0434\u043e\u0431\u043d\u043e\u0439 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0439 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043d\u0443\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0443\u0447\u0435\u0441\u0442\u044c \u0441\u0445\u0435\u043c\u0443 \u0440\u0430\u0437\u0434\u0430\u0447\u0438 \u0441\u0442\u0430\u0442\u0438\u043a\u0438 \u043d\u0435\u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u043c \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c. <code>CDN_BUCKET_PREFIX<\/code> \u0441\u0434\u0435\u043b\u0430\u043d \u0434\u043b\u044f \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0440\u0430\u0431\u043e\u0442\u044b \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432 \/ \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0445 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u0432 \u0440\u0430\u043c\u043a\u0430\u0445 \u043e\u0434\u043d\u043e\u0433\u043e \u0438 \u0442\u043e\u0433\u043e \u0436\u0435 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430. <\/p>\n<p>  <\/p>\n<p>\u0414\u0430\u043b\u0435\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c <a href=\"https:\/\/github.com\/aws\/aws-sdk-js\" rel=\"nofollow\">aws-sdk<\/a>, \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f \u0438 \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043c\u0435\u0442\u043e\u0434\u044b \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f bucket \u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0444\u0430\u0439\u043b\u043e\u0432:<\/p>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">server\/serverUtils\/s3.ts<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">import S3 from 'aws-sdk\/clients\/s3';  import { env } from '..\/..\/env';  export const s3Options = {   credentials: {     accessKeyId: env.CDN_ACCESS_KEY_ID,     secretAccessKey: env.CDN_SECRET_ACCESS_KEY,   },   region: 'ru-central1',   endpoint: env.CDN_ENDPOINT,   httpOptions: {     connectTimeout: 1000 * 60,   }, };  export const s3 = new S3(s3Options);  export function logMessage(...consoleArgs: any[]) {   return function promiseHandler(arg: any) {     if (env.LOGS_CDN) console.log(...consoleArgs);      return arg;   }; }  export function createCDNBucket({ Bucket }: { Bucket: string }) {   logMessage(`Try to create bucket:${Bucket}`);    return Promise.resolve()     .then(() =&gt; s3.headBucket({ Bucket }).promise())     .then(logMessage(`Bucket:${Bucket} already exists`))     .catch(err =&gt; {       \/\/ bucket does not exist, create one       if (err.statusCode &gt;= 400 &amp;&amp; err.statusCode &lt; 500) {         return Promise.resolve()           .then(() =&gt; s3.createBucket({ Bucket, ACL: 'public-read' }).promise())           .then(logMessage(`Bucket:${Bucket} created`));       }        throw err;     }); }  export function uploadToCDNBucket({   Bucket,   Expires,   fileName,   fileContent,   ContentType,   ContentEncoding, }: {   Bucket: string;   Expires?: any;   fileName: string;   fileContent: any;   ContentType: string;   ContentEncoding?: string; }) {   return Promise.resolve()     .then(logMessage(`Try to upload file ${fileName} to bucket:${Bucket}`))     .then(() =&gt;       s3         .putObject({           ACL: 'public-read',           Key: fileName,           Body: fileContent,           Bucket,           Expires,           ContentType,           ContentEncoding,         })         .promise()     )     .then(logMessage(`File ${fileName} uploaded to bucket:${Bucket}`)); }<\/code><\/pre>\n<\/div><\/div>\n<p>  <\/p>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0432 \u0440\u0435\u0446\u0435\u043f\u0442 \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0432\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u043b\u043e\u0433\u0438\u043a\u0443 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0444\u0430\u0439\u043b\u043e\u0432:<\/p>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">server\/serverUtils\/copyAssetsToCDN.ts<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">import fs from 'fs'; import path from 'path';  import mime from 'mime-types';  import { readDirRecursive } from 'serverUtils';  import { env } from '..\/..\/env'; import { paths } from '..\/..\/paths'; import { configEntryServer } from '..\/..\/webpack-custom\/configs\/configEntryServer';  import { createCDNBucket, uploadToCDNBucket } from '.\/s3';  const Bucket = `${env.CDN_BUCKET_PREFIX}${env.CDN_BUCKET}`;  function uploadBuildFile(filePath) {   const fileRelativeName = filePath     .replace(`${paths.buildPath}${path.sep}`, '')     .replace(\/\\\\\/g, '\/');    let ContentEncoding;   if (\/\\.br\/.test(filePath)) ContentEncoding = 'br';   if (\/\\.gz\/.test(filePath)) ContentEncoding = 'gzip';    let ContentType = mime.lookup(fileRelativeName) || 'application\/octet-stream';   if (\/\\.css\/.test(filePath)) ContentType = 'text\/css';   if (\/\\.js\/.test(filePath)) ContentType = 'application\/javascript';    const Expires = new Date(Date.now() + 1000 * 60 * 60 * 24 * 30); \/\/ 1 month    return Promise.resolve()     .then(() =&gt; fs.promises.readFile(filePath))     .then(fileContent =&gt;       uploadToCDNBucket({         Bucket,         Expires,         fileName: fileRelativeName,         fileContent,         ContentType,         ContentEncoding,       })     ); }  export function copyAssetsToCDN() {   if (!env.CDN_ENABLED) return Promise.resolve();    const startTime = Date.now();    const excludedFilenames = Object.keys(configEntryServer).map(fileName =&gt; `${fileName}.js`);    return Promise.resolve()     .then(() =&gt; createCDNBucket({ Bucket }))     .then(() =&gt; readDirRecursive(paths.buildPath))     .then(buildFilesPaths =&gt;       buildFilesPaths.filter(         filePath =&gt; !excludedFilenames.some(fileName =&gt; filePath.includes(fileName))       )     )     .then(buildFilesPaths =&gt; Promise.all(buildFilesPaths.map(uploadBuildFile)))     .then(() =&gt; {       const endTime = Date.now();        console.log(         `Files from build folder have been uploaded to CDN within ${           (endTime - startTime) \/ 1000         } seconds`       );     }); }<\/code><\/pre>\n<\/div><\/div>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">server\/server.ts<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">import { createServer, copyAssetsToCDN } from 'serverUtils'; import { handleFileRoutes } from 'routeMiddlewares\/handleFileRoutes'; import { handlePageRoutes } from 'routeMiddlewares\/handlePageRoutes'; import { handleMissingRoutes } from 'routeMiddlewares\/handleMissingRoutes';  Promise.resolve()   .then(copyAssetsToCDN)   .then(() =&gt; createServer())   .then(server =&gt; server.useMiddlewares([handleFileRoutes, handlePageRoutes, handleMissingRoutes]))   .then(server =&gt; server.start())   .catch(error =&gt; {     console.error(error);      \/\/ eslint-disable-next-line no-process-exit     process.exit(1);   });<\/code><\/pre>\n<\/div><\/div>\n<p>  <\/p>\n<p>\u041b\u043e\u0433\u0438\u043a\u0430 \u043a\u043e\u0434\u0430 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0430\u044f:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u0440\u0435\u043a\u0443\u0440\u0441\u0438\u0432\u043d\u043e \u0441\u0447\u0438\u0442\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043f\u0430\u043f\u043a\u0430 \u0441 \u0430\u0440\u0442\u0435\u0444\u0430\u043a\u0442\u0430\u043c\u0438 (build), \u0438\u0441\u043a\u043b\u044e\u0447\u0430\u044f \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u044b\u0435 \u0444\u0430\u0439\u043b\u044b<\/li>\n<li>\u0434\u043b\u044f \u0441\u0436\u0430\u0442\u044b\u0445 \u0444\u0430\u0439\u043b\u043e\u0432 \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 ContentEncoding<\/li>\n<li>\u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u0444\u0430\u0439\u043b\u043e\u0432 \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442\u0441\u044f ContentType (\u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 <a href=\"https:\/\/github.com\/jshttp\/mime-types\" rel=\"nofollow\">mime-types<\/a> \u0432 \u043e\u0431\u0449\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442 \u0435\u0433\u043e \u0438\u0441\u0445\u043e\u0434\u044f \u0438\u0437 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f \u0444\u0430\u0439\u043b\u0430, \u043d\u043e \u0434\u043b\u044f \u0441\u0436\u0430\u0442\u044b\u0445 \u0444\u0430\u0439\u043b\u043e\u0432, \u0438\u043c\u0435\u044e\u0449\u0438\u0445 \u0434\u0432\u043e\u0439\u043d\u043e\u0435 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435, \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0442\u044c \u0432\u0440\u0443\u0447\u043d\u0443\u044e)<\/li>\n<li>\u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442\u0441\u044f Expire-\u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u043d\u0441\u0438\u0432\u043d\u043e\u0433\u043e \u043a\u0435\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f (\u043a\u0430\u043a \u0430\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 CacheControl, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432 \u043c\u043e\u0435\u0439 \u0432\u0435\u0440\u0441\u0438\u0438 CDN \u043d\u0435 \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442\u0441\u044f)<\/li>\n<li>\u0444\u0430\u0439\u043b \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442\u0441\u044f \u043d\u0430 CDN \u0441 \u043e\u0442\u043d\u043e\u0441\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u043c \u043f\u0443\u0442\u0435\u043c \u043e\u0442 \u043f\u0430\u043f\u043a\u0438 build \u0438 \u043f\u0440\u044f\u043c\u044b\u043c\u0438 \u0441\u043b\u0435\u0448\u0430\u043c\u0438 \u0432 \u043f\u0443\u0442\u0438, \u0442.\u043a. \u0441 \u043e\u0431\u0440\u0430\u0442\u043d\u044b\u043c\u0438 \u043d\u0435 \u0431\u0443\u0434\u0443\u0442 \u0441\u043e\u0437\u0434\u0430\u043d\u044b \u043f\u043e\u0434\u043f\u0430\u043f\u043a\u0438 \u0432 bucket.<\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043f\u0440\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0435 \/ \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a\u0435 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0441 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u043e\u0439 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u043e\u0439 <code>CDN_ENABLED<\/code> \u0444\u0430\u0439\u043b\u044b \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u044e\u0442\u0441\u044f \u0432 \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0435\u0435 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435.<\/p>\n<p>  <\/p>\n<h3 id=\"otdacha-szhatyh-faylov\">\u041e\u0442\u0434\u0430\u0447\u0430 \u0441\u0436\u0430\u0442\u044b\u0445 \u0444\u0430\u0439\u043b\u043e\u0432<\/h3>\n<p>  <\/p>\n<p>\u041f\u0440\u0438 \u043e\u0442\u0434\u0430\u0447\u0435 html-\u0448\u0430\u0431\u043b\u043e\u043d\u0430 \u043d\u0430 \u043f\u0435\u0440\u0432\u043e\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u043e\u0442 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u043f\u0440\u0438 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u043e\u0439 \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u0441\u0436\u0430\u0442\u044b\u0445 \u0444\u0430\u0439\u043b\u043e\u0432 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u044c \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f \u0444\u0430\u0439\u043b\u043e\u0432 \u0432 \u0442\u0435\u0433\u0430\u0445 script \u0438 link \u043d\u0430 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0435 \u0432\u0435\u0440\u0441\u0438\u0438. \u0412\u0430\u0436\u043d\u043e \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u0443\u0447\u0438\u0442\u044b\u0432\u0430\u0442\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u0441 \u043d\u0438\u043c\u0438 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c, \u043e \u0447\u0435\u043c \u043e\u043d \u0441\u043e\u043e\u0431\u0449\u0430\u0435\u0442 \u0432 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0435 Accept-Encoding:<\/p>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">server\/serverUtils\/templateModifiers.ts<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">const compressions = [   {     encoding: 'br',     extension: 'br',   },   {     encoding: 'gzip',     extension: 'gz',   }, ];  export function injectCompressed(req, str) {   const acceptedEncodings = req.acceptsEncodings();   const acceptedCompression = env.GENERATE_COMPRESSED     ? compressions.find(({ encoding }) =&gt; acceptedEncodings.includes(encoding))     : null;    return !acceptedCompression     ? str     : str         .replace(\/\\.js\/g, `.js.${acceptedCompression.extension}`)         .replace(\/\\.css\/g, `.css.${acceptedCompression.extension}`); }<\/code><\/pre>\n<\/div><\/div>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">server\/routeMiddlewares\/handlePageRoutes.ts<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">import fs from 'fs'; import path from 'path';  import { injectAppMarkup, injectBrowserReload, injectCompressed } from 'serverUtils';  import { paths } from '..\/..\/paths';  const template = fs.readFileSync(path.resolve(paths.buildPath, 'template.html'), 'utf-8');  export function handlePageRoutes(app) {   app.get('*', (req, res) =&gt; {     Promise.resolve()       .then(() =&gt; injectAppMarkup(template))       .then(modTemplate =&gt; injectCompressed(req, modTemplate))       .then(modTemplate =&gt; injectBrowserReload(modTemplate))       .then(modTemplate =&gt; res.send(modTemplate))       .catch(error =&gt; {         console.error(error);          res.status(500);         res.send('Unpredictable error');       });   }); }<\/code><\/pre>\n<\/div><\/div>\n<p>  <\/p>\n<p>CDN \u043d\u0435 \u043c\u043e\u0436\u0435\u0442 \u0441\u0430\u043c \u043e\u0442\u0434\u0430\u0432\u0430\u0442\u044c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u0443\u044e \u0441\u0436\u0430\u0442\u0443\u044e \u0432\u0435\u0440\u0441\u0438\u044e, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u0430 \u044d\u0442\u0430 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u0430\u044f \u043c\u043e\u0434\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0448\u0430\u0431\u043b\u043e\u043d\u0430. \u041f\u0440\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u043a\u0435\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u044d\u0442\u043e \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0434\u043e\u043b\u0436\u043d\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c\u0441\u044f \u043f\u043e\u0441\u043b\u0435 \u043d\u0435\u0435, \u0447\u0442\u043e\u0431\u044b \u043a\u043b\u0438\u0435\u043d\u0442\u0443 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e \u043d\u0435 \u0431\u044b\u043b\u0438 \u0432\u044b\u0434\u0430\u043d\u044b \u0444\u0430\u0439\u043b\u044b \u0432 \u043d\u0435\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u043e\u043c \u0444\u043e\u0440\u043c\u0430\u0442\u0435.<\/p>\n<p>  <\/p>\n<h3 id=\"otdacha-faylov-pri-vyklyuchennom-cdn\">\u041e\u0442\u0434\u0430\u0447\u0430 \u0444\u0430\u0439\u043b\u043e\u0432 \u043f\u0440\u0438 \u0432\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u043e\u043c CDN<\/h3>\n<p>  <\/p>\n<p>\u041f\u0440\u0435\u0434\u043f\u043e\u043b\u0430\u0433\u0430\u0435\u0442\u0441\u044f, \u0447\u0442\u043e \u0434\u0430\u043d\u043d\u044b\u0439 \u0440\u0435\u0436\u0438\u043c \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u0438 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0439 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435, \u0430 \u0441\u0436\u0430\u0442\u0438\u0435 \u0444\u0430\u0439\u043b\u043e\u0432 \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u0431\u0443\u0434\u0435\u0442 \u0432\u043a\u043b\u044e\u0447\u0430\u0442\u044c\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u0434\u0435\u0431\u0430\u0433\u0430. \u0422\u0435\u043c \u043d\u0435 \u043c\u0435\u043d\u0435\u0435, \u043f\u0440\u0438 \u0441\u0431\u043e\u0435 \u0440\u0430\u0431\u043e\u0442\u044b CDN \u043c\u043e\u0436\u043d\u043e \u043b\u0435\u0433\u043a\u043e \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435 env-\u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u0438 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u044d\u0442\u043e\u0442 \u0440\u0435\u0436\u0438\u043c.<\/p>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">server\/routeMiddlewares\/handleFileRoutes.ts<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">import serveStatic from 'serve-static';  import { env } from '..\/..\/env';  export const compressions = [   {     encoding: 'br',     extension: 'br',   },   {     encoding: 'gzip',     extension: 'gz',   }, ];  function redirectToCdn(req, res, next) {   return req.originalUrl.includes('.')     ? res.redirect(`${env.CDN_ENDPOINT}\/${env.CDN_BUCKET_PREFIX}${env.CDN_BUCKET}${req.url}`)     : next(); }  function setContentType(contentType) {   return (req, res, next) =&gt; {     res.set('Content-Type', contentType);      return next();   }; }  function setEncoding(encoding) {   return (req, res, next) =&gt; {     res.set('Content-Encoding', encoding);      return next();   }; }  export function handleFileRoutes(app) {   if (env.CDN_ENABLED) return app.get('*', redirectToCdn);    compressions.forEach(({ encoding, extension }) =&gt; {     app.get(`*.js.${extension}`, setContentType('application\/javascript'));     app.get(`*.js.${extension}`, setEncoding(encoding));      app.get(`*.css.${extension}`, setContentType('text\/css'));     app.get(`*.css.${extension}`, setEncoding(encoding));   });    app.use(serveStatic('build'));    \/\/ Send 404 for all not found files   app.get('*', (req, res, next) =&gt; (req.originalUrl.includes('.') ? res.sendStatus(404) : next())); }<\/code><\/pre>\n<\/div><\/div>\n<p>  <\/p>\n<p>\u0421\u0445\u0435\u043c\u0430 \u0440\u0430\u0431\u043e\u0442\u044b:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u0435\u0441\u043b\u0438 CDN \u0432\u043a\u043b\u044e\u0447\u0435\u043d, \u0442\u043e \u0432\u0441\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043d\u0430 \u0444\u0430\u0439\u043b\u044b \u043f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u043d\u0430 \u043d\u0435\u0433\u043e, \u0442.\u0435. \u043d\u0430\u0448 \u0441\u0435\u0440\u0432\u0435\u0440 \u0432\u044b\u0441\u0442\u0443\u043f\u0430\u0435\u0442 \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043f\u0440\u043e\u043a\u0441\u0438. \u042d\u0442\u043e\u0442 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b \u043f\u0440\u0438\u0433\u043e\u0436\u0434\u0430\u0435\u0442\u0441\u044f \u0440\u0435\u0434\u043a\u043e, \u0442\u0430\u043a \u043a\u0430\u043a \u0446\u0435\u043b\u044c \u2014 \u043a\u0430\u043a \u0440\u0430\u0437 \u0438\u0437\u0431\u0435\u0433\u0430\u0442\u044c \u043f\u043e\u0434\u043e\u0431\u043d\u043e\u0433\u043e \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u044f, \u043d\u043e \u0438\u043d\u043e\u0433\u0434\u0430 \u043d\u0443\u0436\u043d\u043e \u0438\u043c\u0435\u0442\u044c \u0441\u0441\u044b\u043b\u043a\u0443 \u043d\u0430 \u0444\u0430\u0439\u043b \u043e\u0442\u043d\u043e\u0441\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0433\u043b\u0430\u0432\u043d\u043e\u0433\u043e \u0434\u043e\u043c\u0435\u043d\u0430 (\u0434\u043b\u044f \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f \u043f\u0440\u0430\u0432, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440);<\/li>\n<li>\u0435\u0441\u043b\u0438 \u0432\u044b\u043a\u043b\u044e\u0447\u0435\u043d, \u0442\u043e \u043f\u0440\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0435 \u043d\u0430 \u0444\u0430\u0439\u043b\u044b .gz \u0438 .br \u043f\u0440\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0435 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438;<\/li>\n<li>\u0435\u0441\u043b\u0438 \u0444\u0430\u0439\u043b \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u0438 CDN \u0432\u043a\u043b\u044e\u0447\u0435\u043d, \u0442\u043e \u0441\u0442\u0430\u0442\u0443\u0441 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0438 \u043a\u043e\u043d\u0442\u0435\u043d\u0442 \u043e\u0448\u0438\u0431\u043e\u043a \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u043e\u043d \u0441\u0430\u043c, \u0430 \u0435\u0441\u043b\u0438 \u0432\u044b\u043a\u043b\u044e\u0447\u0435\u043d \u2014 \u0442\u043e \u043e\u0448\u0438\u0431\u043a\u0430 404 \u043e\u0442\u0434\u0430\u0435\u0442\u0441\u044f \u043d\u0430\u0448\u0438\u043c \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c.<\/li>\n<\/ul>\n<p>  <\/p>\n<h3 id=\"pryamye-ssylki-na-cdn\">\u041f\u0440\u044f\u043c\u044b\u0435 \u0441\u0441\u044b\u043b\u043a\u0438 \u043d\u0430 CDN<\/h3>\n<p>  <\/p>\n<p>\u041d\u0430 \u0434\u0430\u043d\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 \u0444\u0430\u0439\u043b\u044b \u0441\u0436\u0430\u0442\u044b \u0438 \u043b\u0435\u0436\u0430\u0442 \u043d\u0430 \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0435\u043c \u0441\u0435\u0440\u0432\u0435\u0440\u0435, \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u043e\u0441\u0442\u0430\u043b\u043e\u0441\u044c \u043b\u0438\u0448\u044c \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u0441\u0441\u044b\u043b\u043a\u0438 \u0441 \u043e\u0442\u043d\u043e\u0441\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u043e\u0442 \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u0434\u043e\u043c\u0435\u043d\u0430. \u0417\u0430\u043c\u0435\u043d\u043e\u0439 \u0448\u0430\u0431\u043b\u043e\u043d\u0430 \u043a\u0430\u043a \u0432\u044b\u0448\u0435 \u044d\u0442\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043d\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u0441\u044f, \u0442\u0430\u043a \u043a\u0430\u043a \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0440\u0435\u0441\u0443\u0440\u0441\u044b (\u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440) \u0438 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0435 \u0447\u0430\u043d\u043a\u0438 \u043c\u043e\u0433\u0443\u0442 \u0442\u0430\u043c \u043d\u0435 \u043f\u0440\u0438\u0441\u0443\u0442\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u043e \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u044c publicPath \u0432 \u043a\u043e\u043d\u0444\u0438\u0433\u0435 Webpack:<\/p>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">webpack-custom\/configs\/configOutput.ts<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">import webpack from 'webpack';  import { env } from '..\/..\/env'; import { paths } from '..\/..\/paths';  const publicPath = !env.CDN_ENABLED   ? '\/'   : `${env.CDN_ENDPOINT}\/${env.CDN_BUCKET_PREFIX}${env.CDN_BUCKET}\/`;  export const configOutput: webpack.Configuration['output'] = {   path: paths.buildPath,   filename: env.FILENAME_HASH ? '[name].[contenthash].js' : '[name].js',   publicPath, };<\/code><\/pre>\n<\/div><\/div>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">webpack-custom\/configs\/configOutputServer.ts<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">import webpack from 'webpack';  import { env } from '..\/..\/env'; import { paths } from '..\/..\/paths';  const publicPath = !env.CDN_ENABLED   ? '\/'   : `${env.CDN_ENDPOINT}\/${env.CDN_BUCKET_PREFIX}${env.CDN_BUCKET}\/`;  export const configOutputServer: webpack.Configuration['output'] = {   path: paths.buildPath,   filename: '[name].js', \/\/ static name for server build   publicPath, };<\/code><\/pre>\n<\/div><\/div>\n<p>  <\/p>\n<p>\u042d\u0442\u043e \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0439 \u0448\u0430\u0433 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438, \u0432 \u0438\u0442\u043e\u0433\u0435 \u043f\u0440\u0438 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u044b\u0445 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0445 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u0445 \u043f\u0440\u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0435 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0441\u0441\u044b\u043b\u043a\u0438 \u043d\u0430 \u0440\u0435\u0441\u0443\u0440\u0441\u044b \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0433\u043e \u0432\u0438\u0434\u0430:<\/p>\n<p>  <\/p>\n<pre><code class=\"html\">&lt;script src=&quot;https:\/\/storage.yandexcloud.net\/myproject-bucket-production\/client.67132bc5b2cbe6df5b5e.js.br&quot;&gt;&lt;\/script&gt;<\/code><\/pre>\n<p>  <\/p>\n<p>\u0418 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0432\u0441\u0435 \u043f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u0430 \u2014 \u0445\u043e\u0440\u043e\u0448\u0443\u044e \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u0438\u0440\u0443\u0435\u043c\u0443\u044e \u043a\u043e\u043c\u043f\u0440\u0435\u0441\u0441\u0438\u044e, \u0443\u0441\u043a\u043e\u0440\u0435\u043d\u0438\u0435 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0430\u0441\u0441\u0435\u0442\u043e\u0432 \u0438\u0437 \u0434\u0440\u0443\u0433\u0438\u0445 \u0440\u0435\u0433\u0438\u043e\u043d\u043e\u0432, \u0443\u043c\u0435\u043d\u044c\u0448\u0435\u043d\u0438\u0435 \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0441\u0435\u0440\u0432\u0435\u0440 \u0438, \u043a\u0430\u043a \u043f\u0440\u0430\u0432\u0438\u043b\u043e, \u0431\u043e\u043b\u0435\u0435 \u0434\u0435\u0448\u0435\u0432\u044b\u0439 \u0442\u0440\u0430\u0444\u0438\u043a, \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u043d\u0435 \u043f\u043e\u0441\u0442\u0443\u043f\u0430\u044f\u0441\u044c \u0443\u0434\u043e\u0431\u0441\u0442\u0432\u043e\u043c \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0439 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0432 \u0440\u0435\u0436\u0438\u043c\u0435 \u043e\u0442\u0434\u0430\u0447\u0438 \u0441\u0442\u0430\u0442\u0438\u043a\u0438 \u0441\u0432\u043e\u0438\u043c \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c.<\/p>\n<p>  <\/p>\n<p>\u041e\u0441\u0442\u0430\u043b\u0441\u044f \u043b\u0438\u0448\u044c \u043e\u0434\u0438\u043d \u043e\u0447\u0435\u0432\u0438\u0434\u043d\u044b\u0439 \u0441\u0430\u0439\u0434-\u044d\u0444\u0444\u0435\u043a\u0442: \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u043c\u044b\u0435 \u0447\u0430\u043d\u043a\u0438 \u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u044e\u0442\u0441\u044f \u0432 \u043d\u0435\u0441\u0436\u0430\u0442\u044b\u0445 \u0432\u0435\u0440\u0441\u0438\u044f\u0445, \u0442\u0430\u043a \u043a\u0430\u043a \u043d\u0435 \u043f\u0440\u043e\u0445\u043e\u0434\u044f\u0442 \u0447\u0435\u0440\u0435\u0437 \u0441\u0435\u0440\u0432\u0435\u0440, \u0441\u043f\u043e\u0441\u043e\u0431\u043d\u044b\u0439 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c Accept-Encoding \u0438 \u043f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u043d\u0430 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0443\u044e \u0432\u0435\u0440\u0441\u0438\u044e. \u041a\u0430\u043a \u043f\u0440\u0430\u0432\u0438\u043b\u043e, \u0432 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430\u0445, \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0449\u0438\u0445 \u043f\u043e lazy-\u043f\u0430\u0442\u0442\u0435\u0440\u043d\u0443, \u0443\u0436\u0435 \u0437\u0430\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c\u044b \u0434\u043b\u044f \u0441\u043c\u044f\u0433\u0447\u0435\u043d\u0438\u044f \u043f\u0430\u0434\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u043f\u0440\u0438 \u0434\u043e\u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0435, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0435 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u0432\u0440\u0435\u043c\u044f (\u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u043c\u0435\u0442\u043d\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u0438 \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u043e\u043c \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442\u0435 \u0443 \u043a\u043b\u0438\u0435\u043d\u0442\u0430) \u043d\u0435 \u0441\u0442\u0430\u043d\u0435\u0442 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u043e\u0439. \u041d\u043e \u0442\u0430\u043a \u043a\u0430\u043a \u0432 \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432 \u0442\u0430\u043a\u043e\u0439 \u043f\u0430\u0442\u0442\u0435\u0440\u043d \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f, \u0442\u043e \u043b\u0438\u0448\u044c \u043d\u0435\u043c\u043d\u043e\u0433\u0438\u0435 \u0441\u0442\u043e\u043b\u043a\u043d\u0443\u0442\u0441\u044f \u0441 \u044d\u0442\u0438\u043c.<\/p>\n<p>  <\/p>\n<p>\u0421\u043e\u0432\u0435\u0442 \u043d\u0430\u043f\u043e\u0441\u043b\u0435\u0434\u043e\u043a \u2014 \u043d\u0435 \u043f\u043e\u043b\u0435\u043d\u0438\u0442\u0435\u0441\u044c \u0438\u0437\u043c\u0435\u0440\u0438\u0442\u044c \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u043e\u0442\u0434\u0430\u0447\u0438 \u0441\u0442\u0430\u0442\u0438\u043a\u0438 \u0432 \u0442\u0435\u043a\u0443\u0449\u0435\u043c \u0440\u0435\u0436\u0438\u043c\u0435 \u0440\u0430\u0431\u043e\u0442\u044b \u0432 \u0432\u0430\u0448\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0435 \u043f\u0440\u0438 \u043e\u0442\u043a\u0440\u044b\u0442\u0438\u0438 \u0441\u0430\u0439\u0442\u0430 \u0438\u0437 \u0446\u0435\u043b\u0435\u0432\u044b\u0445 \u0440\u0435\u0433\u0438\u043e\u043d\u043e\u0432 (\u0435\u0441\u0442\u044c \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b, \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0449\u0438\u0435 \u044d\u0442\u0443 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u043e). \u041d\u0435 \u0432\u0441\u0435 CDN-\u0441\u0435\u0440\u0432\u0435\u0440\u044b \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u043e \u0445\u043e\u0440\u043e\u0448\u0438, \u0438, \u043d\u0435\u0441\u043c\u043e\u0442\u0440\u044f \u043d\u0430 \u043e\u0447\u0435\u0432\u0438\u0434\u043d\u044b\u0435 \u043f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u0430 &quot;\u043d\u0430 \u0431\u0443\u043c\u0430\u0433\u0435&quot;, \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043d\u0430 \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0435 \u043d\u0435 \u0432\u0441\u0435\u0433\u0434\u0430 \u0432\u043f\u0435\u0447\u0430\u0442\u043b\u044f\u044e\u0449 \u043f\u0440\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 \u0432\u044b\u0441\u043e\u043a\u043e\u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e\u0433\u043e \u0438 \u0431\u044b\u0441\u0442\u0440\u043e\u0433\u043e \u0445\u043e\u0441\u0442\u0438\u043d\u0433\u0430 \u0438 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u043c \u043f\u043e\u0442\u043e\u043a\u0435 \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432 \u0438\u0437 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0440\u0435\u0433\u0438\u043e\u043d\u0430.<\/p>\n<p>  <\/p>\n<p><a href=\"https:\/\/github.com\/dkazakov8\/habr_cdn-with-compression\" rel=\"nofollow\">\u0420\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439<\/a><\/p>\n<p>  <\/p>\n<p>\u0412\u044b\u0441\u043e\u043a\u043e\u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0438\u043d\u0433\u0430!<\/p>\n<\/div>\n<p> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/post\/507402\/\"> https:\/\/habr.com\/ru\/post\/507402\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"\n<div class=\"post__text post__text-html post__text_v1\" id=\"post-content-body\" data-io-article-url=\"https:\/\/habr.com\/ru\/post\/507402\/\">\n<p>\u041f\u0440\u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0435 \u0441\u0430\u0439\u0442\u0430 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440 \u043b\u043e\u0436\u0438\u0442\u0441\u044f \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e \u0437\u0430\u0434\u0430\u0447, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u0431\u044b\u0441\u0442\u0440\u043e \u0438 \u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u043e. \u041d\u043e \u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0441\u0442\u044c \u0437\u0430 \u0447\u0430\u0441\u0442\u044c \u0438\u0437 \u043d\u0438\u0445 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043d\u0430 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0444\u0430\u0439\u043b\u043e\u0432, \u0438\u0445 \u043f\u0435\u0440\u0435\u0441\u044b\u043b\u043a\u0443 \u043a\u043b\u0438\u0435\u043d\u0442\u0443 \u0438 \u043a\u043e\u043c\u043f\u0440\u0435\u0441\u0441\u0438\u044e \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0435\u043c\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445) \u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u043b\u043e\u0436\u0438\u0442\u044c \u043d\u0430 \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u044b\u0435 \u0444\u0430\u0439\u043b\u043e\u0432\u044b\u0435 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430. \u041e\u043d\u0438, \u043a\u0430\u043a \u043f\u0440\u0430\u0432\u0438\u043b\u043e, \u0438\u043c\u0435\u044e\u0442 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0442\u0435\u0440\u0440\u0438\u0442\u043e\u0440\u0438\u0430\u043b\u044c\u043d\u044b\u0445 \u0437\u043e\u043d \u0438 \u043e\u0442\u0434\u0430\u044e\u0442 \u043a\u043b\u0438\u0435\u043d\u0442\u0443 \u0444\u0430\u0439\u043b\u044b \u043e\u0442 \u043d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u0431\u043b\u0438\u0437\u043a\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0435\u0440\u0430 (\u0442\u0430\u043a, \u0432\u0440\u0435\u043c\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432 \u0438\u0437 \u0444\u0438\u0437\u0438\u0447\u0435\u0441\u043a\u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u0445 \u043e\u0442 \u0440\u0430\u0441\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u043c\u0435\u0441\u0442 \u0437\u043d\u0430\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0441\u043e\u043a\u0440\u0430\u0442\u0438\u0442\u0441\u044f). \u042d\u0442\u0438 \u043f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f CDN \u2014 \u0440\u0430\u0437\u0433\u0440\u0443\u0437\u043a\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0430, \u0441\u043e\u043a\u0440\u0430\u0449\u0435\u043d\u0438\u0435 \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0434\u043e\u0441\u0442\u0430\u0432\u043a\u0438 \u043a\u043e\u043d\u0442\u0435\u043d\u0442\u0430, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0441\u043e\u043a\u0440\u0430\u0449\u0435\u043d\u0438\u0435 \u0442\u0440\u0430\u0444\u0438\u043a\u0430, \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0435\u043c\u043e\u0433\u043e \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u043c \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c (\u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0431\u044b\u0447\u043d\u043e \u0434\u043e\u0440\u043e\u0436\u0435), \u043f\u0440\u0438\u0432\u0435\u043b\u0438 \u043a \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0448\u0438\u0440\u043e\u043a\u043e\u043c\u0443 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044e \u043f\u043e\u0434\u043e\u0431\u043d\u043e\u0433\u043e \u043f\u043e\u0434\u0445\u043e\u0434\u0430. \u0421\u0435\u0433\u043e\u0434\u043d\u044f \u0440\u0430\u0437\u0431\u0435\u0440\u0435\u043c\u0441\u044f, \u043a\u0430\u043a \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 Amazon S3-\u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u044b\u043c\u0438 CDN \u0438 \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0442\u044c \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0443 \u0441\u0436\u0430\u0442\u044b\u0445 \u0444\u0430\u0439\u043b\u043e\u0432.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-305698","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/305698","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=305698"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/305698\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=305698"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=305698"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=305698"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}