{"id":304539,"date":"2020-05-30T09:00:17","date_gmt":"2020-05-30T09:00:17","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=304539"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=304539","title":{"rendered":"\u041f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u0443\u0435\u043c \u043f\u0440\u044f\u043c\u043e \u0432 Nginx"},"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\/company\/vdsina\/blog\/504308\/\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/3d\/1p\/2x\/3d1p2x0yrvkljpmvhyvrgjzlsre.jpeg\"><\/p>\n<p>  Nginx \u2014 \u0432\u0435\u043b\u0438\u043a\u043e\u043b\u0435\u043f\u043d\u044b\u0439 \u0432\u0435\u0431-\u0441\u0435\u0440\u0432\u0435\u0440. \u0412\u0441\u0435 \u043c\u044b \u043f\u0440\u0438\u0432\u044b\u043a\u043b\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0435\u0433\u043e \u0432 \u0441\u0432\u044f\u0437\u043a\u0435 \u0441 \u0431\u0435\u043a\u0435\u043d\u0434\u043e\u043c\u0430\u043c\u0438 \u043d\u0430 \u0440\u0430\u0437\u043d\u044b\u0445 \u044f\u0437\u044b\u043a\u0430\u0445 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f. \u041d\u043e \u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043c\u043e\u0436\u043d\u043e \u043f\u0438\u0441\u0430\u0442\u044c \u043f\u0440\u043e\u0441\u0442\u044b\u0435 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u044b \u043f\u0440\u044f\u043c\u043e \u0432\u043d\u0443\u0442\u0440\u0438 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u043e\u0433\u043e \u0444\u0430\u0439\u043b\u0430 Nginx. \u042d\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u043b\u044f \u0431\u0430\u043b\u0430\u043d\u0441\u0438\u0440\u043e\u0432\u043a\u0438, \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u043f\u0440\u043e\u0441\u0442\u044b\u0445 API \u0438 \u0434\u0430\u0436\u0435 \u043e\u0442\u0434\u0430\u0432\u0430\u0442\u044c \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u043f\u0440\u044f\u043c\u043e \u0438\u0437 \u043a\u043e\u043d\u0444\u0438\u0433\u0430. <\/p>\n<p>  \u0412 \u0441\u0442\u0430\u0442\u044c\u0435 \u043c\u044b \u0440\u0430\u0437\u0431\u0435\u0440\u0435\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u044b \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c \u0432 \u043a\u043e\u043d\u0444\u0438\u0433\u0435 nginx.<br \/>  <a name=\"habracut\"><\/a><\/p>\n<p>  \u0412\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u044d\u0442\u043e \u043a\u0430\u043a \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u043a\u043e\u0434\u0430 \u0432 \u043a\u043e\u043d\u0444\u0438\u0433\u0435, \u0447\u0442\u043e \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0434\u0438\u043a\u043e\u0432\u0430\u0442\u043e, \u043d\u043e \u0443\u0434\u043e\u0431\u043d\u043e. \u041a\u043e\u0434 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e, \u043d\u0435 \u0432\u043c\u0435\u0448\u0438\u0432\u0430\u044f\u0441\u044c \u0432 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0446\u0438\u043a\u043b \u0441\u043e\u0431\u044b\u0442\u0438\u0439 Nginx, \u0431\u0435\u0437 \u043a\u043e\u043b\u043b\u0431\u044d\u043a\u043e\u0432. \u0420\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0431\u044b\u0441\u0442\u0440\u043e \u0438, \u0447\u0442\u043e \u043d\u0435\u043c\u0430\u043b\u043e\u0432\u0430\u0436\u043d\u043e, \u0432 \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u043e\u0441\u0442\u0438 \u0441 \u0434\u0440\u0443\u0433\u0438\u043c\u0438 \u043c\u043e\u0434\u0443\u043b\u044f\u043c\u0438 \u0438 \u0432\u0441\u0435\u043c \u0431\u0430\u0437\u043e\u0432\u044b\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u043e\u043c.<br \/>  \u041e\u0441\u043d\u043e\u0432\u043d\u044b\u043c \u0440\u0435\u0448\u0435\u043d\u0438\u0435\u043c \u0434\u043b\u044f Lua + Nginx \u0441\u0447\u0438\u0442\u0430\u0435\u0442\u0441\u044f <a href=\"https:\/\/openresty.org\/\">OpenResty<\/a>. \u0422\u0430\u043c \u043c\u043d\u043e\u0433\u043e \u0433\u043e\u0442\u043e\u0432\u044b\u0445 \u043c\u043e\u0434\u0443\u043b\u0435\u0439, \u043a\u0430\u043a \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0445 \u043d\u0430 Lua, \u0442\u0430\u043a \u0438 \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u0438\u0437 Nginx. \u041e\u043d \u043e\u0442\u043b\u0438\u0447\u043d\u043e \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0438 \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442 \u0432\u044b\u0441\u043e\u043a\u0443\u044e \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0438 \u043f\u0440\u043e\u043f\u0443\u0441\u043a\u043d\u0443\u044e \u0441\u043f\u043e\u0441\u043e\u0431\u043d\u043e\u0441\u0442\u044c Nginx.<\/p>\n<h2>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430<\/h2>\n<p>  \u0421\u0430\u043c Nginx \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0442\u044c \u043d\u0435 \u043d\u0443\u0436\u043d\u043e, OpenResty \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u0435\u0433\u043e \u0432 \u0441\u0432\u043e\u044e \u0441\u0431\u043e\u0440\u043a\u0443.<\/p>\n<pre><code class=\"bash\">wget https:\/\/openresty.org\/download\/openresty-1.15.8.3.tar.gz tar -xvf openresty-1.15.8.3.tar.gz cd openresty-1.15.8.3\/ sudo apt-get install build-essential <\/code><\/pre>\n<p>  \u0422\u0430\u043a\u0436\u0435 \u0434\u043e\u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043c \u0435\u0449\u0451 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043f\u0430\u043a\u0435\u0442\u043e\u0432 \u0434\u043b\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0438 CLI, \u0440\u0435\u0433\u0443\u043b\u044f\u0440\u043d\u044b\u0445 \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u0439 \u0438 SSL:  <\/p>\n<pre><code class=\"bash\">sudo apt-get install libreadline-dev libncurses5-dev libpcre3-dev libssl-dev perl<\/code><\/pre>\n<p>  \u0418 \u043d\u0430\u0447\u043d\u0451\u043c \u0441\u0431\u043e\u0440\u043a\u0443  <\/p>\n<pre><code class=\"bash\">.\/configure -j2 --with-pcre-jit --with-ipv6 make -j2 sudo make install <\/code><\/pre>\n<p>  \u041d\u0430\u043a\u043e\u043d\u0435\u0446, \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c OpenResty  <\/p>\n<pre><code class=\"bash\">sudo \/usr\/local\/openresty\/bin\/openresty<\/code><\/pre>\n<p>  \u0412\u044b\u0432\u043e\u0434\u0430 \u043d\u0435 \u043f\u043e\u0441\u043b\u0435\u0434\u0443\u0435\u0442, \u0441\u0435\u0440\u0432\u0435\u0440 \u043f\u0440\u043e\u0441\u0442\u043e \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0441\u044f \u0438 \u0431\u0443\u0434\u0435\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d:<br \/>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/41\/au\/x1\/41aux1dma6mbdyuzlwx85rgwt7q.png\"><\/p>\n<p>  \u041e\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430:  <\/p>\n<pre><code class=\"bash\">sudo \/usr\/local\/openresty\/bin\/openresty -s quit<\/code><\/pre>\n<h2>Hello world<\/h2>\n<p>  \u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044e \u0438 \u043a\u043e\u043d\u0444\u0438\u0433 \u0434\u043b\u044f \u043d\u0430\u0448\u0435\u0433\u043e \u0441\u0430\u0439\u0442\u0430  <\/p>\n<pre><code class=\"bash\">sudo mkdir \/usr\/local\/openresty\/nginx\/sites sudo nano \/usr\/local\/openresty\/nginx\/sites\/default.conf<\/code><\/pre>\n<pre><code class=\"nginx\">server {     listen 80 default_server;     listen [::]:80 default_server;      root \/usr\/local\/openresty\/nginx\/html\/default;      index index.html index.htm;      location \/ {         default_type 'text\/plain';          content_by_lua_file \/usr\/local\/openresty\/nginx\/html\/default\/index.lua;     } }<\/code><\/pre>\n<p>  \u0418\u0441\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u0441\u043a\u0440\u0438\u043f\u0442\u044b \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u044f\u043c\u043e \u0432 \u043a\u043e\u043d\u0444\u0438\u0433\u0435, \u043d\u043e \u0443\u0434\u043e\u0431\u043d\u0435\u0435 \u0441\u0440\u0430\u0437\u0443 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0432\u043d\u0435\u0448\u043d\u0438\u0439 \u0444\u0430\u0439\u043b  <\/p>\n<pre><code class=\"bash\">sudo nano \/usr\/local\/openresty\/nginx\/html\/default\/index.lua<\/code><\/pre>\n<pre><code class=\"lua\">local name = ngx.var.arg_name or &quot;Anonymous&quot; ngx.say(&quot;Hello, &quot;, name, &quot;!&quot;)<\/code><\/pre>\n<pre><code class=\"bash\">sudo mkdir \/usr\/local\/openresty\/nginx\/html\/default sudo mv \/usr\/local\/openresty\/nginx\/html\/index.html \/usr\/local\/openresty\/nginx\/html\/default<\/code><\/pre>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/ta\/sa\/b5\/tasab5st0iygmkleek2z8morluo.png\"><\/p>\n<h2>\u041f\u0440\u0438\u043c\u0435\u0440\u044b<\/h2>\n<p>  \u041d\u0438\u0436\u0435 \u0441\u043e\u0431\u0440\u0430\u043d\u044b \u0431\u043e\u043b\u0435\u0435 \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u043d\u044b\u0435 \u043f\u0440\u0438\u043c\u0435\u0440\u044b \u0438\u0437 \u0440\u0430\u0437\u043d\u044b\u0445 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u043e\u0432:<\/p>\n<h4>ruhighload.com<\/h4>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">\u0412\u044b\u0432\u043e\u0434 HTML<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"nginx\">server {   location \/hello {     default_type 'text\/html';      content_by_lua '         ngx.say(&quot;Hello &lt;b&gt;world&lt;\/b&gt;!&quot;)     ';   } }<\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">\u041d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"nginx\">server {   location \/ {     default_type 'text\/plain';     content_by_lua_file \/var\/www\/lua\/index.lua;   }    location \/admin {     default_type 'text\/plain';     content_by_lua_file \/var\/www\/lua\/admin.lua;   } }<\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">\u0413\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u044b\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"nginx\">http {     # \u043e\u0431\u044a\u044f\u0432\u043b\u044f\u0435\u043c \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u044b\u0439 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440     lua_shared_dict stats 1m;      server {         location \/ {             content_by_lua ' \t\t# \u0443\u0432\u0435\u043b\u0438\u0447\u0438\u043c \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e hits \u043d\u0430 1 \u043f\u0440\u0438 \u043a\u0430\u0436\u0434\u043e\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u0435                 ngx.shared.stats:incr(&quot;hits&quot;, 1) \t\t \t\t# \u0432\u044b\u0432\u0435\u0434\u0435\u043c \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435                 ngx.say(ngx.shared.stats:get(&quot;hits&quot;))             ';         }     } }<\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">\u0421\u043a\u0440\u0438\u043f\u0442 \u0434\u043b\u044f \u043f\u043e\u0434\u0441\u0447\u0435\u0442\u0430 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0432 Redis<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"bash\">apt-get install lua-nginx-redis<\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"nginx\">server {         location \/ {             content_by_lua ' \t\tlocal redis = require &quot;nginx.redis&quot; \t\tlocal red = redis:new() \t\tlocal ok, err = red:connect(&quot;127.0.0.1&quot;, 6379) \t\tok, err = red:incr(&quot;test&quot;) \t\tlocal res, err = red:get(&quot;test&quot;) \t\tngx.say(&quot;hits: &quot;, res)             ';         } }<\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<h4>openresty.org<\/h4>\n<p>  <a href=\"https:\/\/openresty.org\/en\/routing-mysql-queries-based-on-uri-args.html\">Routing MySQL Queries Based On URI Args<\/a><br \/>  <a href=\"https:\/\/openresty.org\/en\/dynamic-routing-based-on-redis.html\">Dynamic Request Routing Based on Redis<\/a><br \/>  <a href=\"https:\/\/github.com\/openresty\/openresty-survey\">Web App for OpenResty User Survey<\/a><br \/>  <a href=\"https:\/\/github.com\/openresty\/openresty.org\">Code and data for the openresty.org site<\/a> \u2014 \u043b\u044e\u0431\u043e\u0439 \u0441\u0430\u0439\u0442, \u043f\u043e\u0441\u0432\u044f\u0449\u0451\u043d\u043d\u044b\u0439 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u043e\u0439 \u0432\u0435\u0431-\u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0438, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0435\u0451, \u0438 openresty.org \u043d\u0435 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/p>\n<h4><a href=\"https:\/\/habr.com\/ru\/post\/270463\/\">habr.com\/ru\/post\/270463<\/a><\/h4>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">\u041f\u043e\u0438\u0441\u043a \u0441 \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"lua\">-- search.lua local string = ngx.var.arg_string  -- \u043f\u043e\u043b\u0443\u0447\u0438\u043c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u0438\u0437 GET \u0437\u0430\u043f\u0440\u043e\u0441\u0430 if string == nil then     ngx.exec(&quot;\/&quot;) -- \u0435\u0441\u043b\u0438 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430 \u043d\u0435\u0442, \u0442\u043e \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u0440\u0435\u0434\u0438\u0440\u0435\u043a\u0442 end  local path = &quot;\/?string=&quot; .. string  local redis = require &quot;resty.redis&quot; -- \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 \u043f\u043e \u0440\u0430\u0431\u043e\u0442\u0435 \u0441 redis local red = redis:new()  red:set_timeout(1000) -- 1 sec  local ok, err = red:connect(&quot;127.0.0.1&quot;, 6379) if not ok then     ngx.exec(path) -- \u0435\u0441\u043b\u0438 \u043d\u0435\u043b\u044c\u0437\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f \u043a redis, \u0442\u043e \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u0440\u0435\u0434\u0438\u0440\u0435\u043a\u0442 end  res, err = red:get(&quot;search:&quot; .. string); -- \u043f\u043e\u043b\u0443\u0447\u0438\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 redis  if res == ngx.null then     ngx.exec(path) -- \u0435\u0441\u043b\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u043d\u0435\u0442, \u0442\u043e \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u0440\u0435\u0434\u0438\u0440\u0435\u043a\u0442 else     ngx.header.content_type = 'application\/json'     ngx.say(res) -- \u0435\u0441\u043b\u0438 \u0434\u0430\u043d\u043d\u044b\u0435 \u0435\u0441\u0442\u044c, \u0442\u043e \u043e\u0442\u0434\u0430\u0434\u0438\u043c \u0438\u0445 end<\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"nginx\"># nginx.conf location \/search-by-string {    content_by_lua_file lua\/search.lua; }<\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<h4><a href=\"https:\/\/habr.com\/ru\/post\/326486\/\">habr.com\/ru\/post\/326486<\/a><\/h4>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">Load balancer<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<blockquote><p>\u0412 \u0431\u043b\u043e\u043a\u0435 http {} \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c lua.<\/p>\n<p>  \u041a\u043e\u0434 \u0441 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u043c\u0438:  <\/p>\n<pre><code class=\"lua\"># \u043f\u0443\u0442\u044c \u0434\u043e \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044b\u0445 *.lua \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a \u0441 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435\u043c \u0441\u0438\u0441\u0442\u0435\u043c\u043d\u044b\u0445 \u043f\u0443\u0442\u0435\u0439 lua_package_path &quot;\/usr\/local\/lib\/lua\/?.lua;;&quot;; init_by_lua_block {     -- \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0433\u043e \u043c\u043e\u0434\u0443\u043b\u044f     -- \u0432 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0435, \u044d\u0442\u043e\u0442 \u0431\u043b\u043e\u043a \u043c\u043e\u0436\u043d\u043e \u043e\u043f\u0443\u0441\u0442\u0438\u0442\u044c     require &quot;resty.core&quot;     collectgarbage(&quot;collect&quot;)  -- just to collect any garbage }<\/code><\/pre>\n<p>  \u0432 \u0431\u043b\u043e\u043a\u0430\u0445 *_lua_block \u0443\u0436\u0435 \u0438\u0434\u0451\u0442 lua-\u043a\u043e\u0434 \u0441\u043e \u0441\u0432\u043e\u0438\u043c \u0441\u0438\u043d\u0442\u0430\u043a\u0441\u0438\u0441\u043e\u043c \u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u044f\u043c\u0438.<\/p>\n<p>  \u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0441\u0435\u0440\u0432\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u043d\u0430 \u0441\u0435\u0431\u044f \u0432\u043d\u0435\u0448\u043d\u0438\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b.<\/p>\n<p>  \u041a\u043e\u0434 \u0441 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u043c\u0438:  <\/p>\n<pre><code class=\"nginx\">server {     listen 80;     server_name test.domain.local;    location \/ {     # \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u043d\u0430\u043b\u0438\u0447\u0438\u0435 cookie &quot;upid&quot; \u0438 \u0435\u0441\u043b\u0438 \u043d\u0435\u0442 \u2014 \u0432\u044b\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u043c \u043f\u043e \u0436\u0435\u043b\u0430\u0435\u043c\u043e\u043c\u0443 \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c\u0443     if ($cookie_upid = &quot;&quot;) {             # \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c \u043f\u0443\u0441\u0442\u0443\u044e \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e nginx-\u0430, \u0432 \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u0437\u0430\u043f\u0438\u0448\u0435\u043c \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0439 ID \u0431\u044d\u043a\u0435\u043d\u0434\u0430             set $upstream_id '';             rewrite_by_lua_block {                 -- \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c \u043c\u0430\u0442\u0435\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0433\u0435\u043d\u0435\u0440\u0430\u0442\u043e\u0440 \u0434\u043b\u044f \u0431\u043e\u043b\u0435\u0435 \u0440\u0430\u043d\u0434\u043e\u043c\u043d\u043e\u0433\u043e \u0440\u0430\u043d\u0434\u043e\u043c\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0432\u0440\u0435\u043c\u044f nginx-\u0430                 math.randomseed(ngx.time())                 -- \u0442\u0430\u043a\u0436\u0435 \u043f\u0440\u043e\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u043f\u0435\u0440\u0432\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0441\u043e\u0432\u0441\u0435\u043c \u043d\u0435 \u0440\u0430\u043d\u0434\u043e\u043c\u043d\u043e\u0435 (\u0441\u043c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044e)                 math.random(100)                 local num = math.random(100)                 -- \u043f\u043e\u043b\u0443\u0447\u0438\u0432 \u0447\u0438\u0441\u043b\u043e, \u0431\u0435\u0441\u0445\u0438\u0442\u0440\u043e\u0441\u0442\u043d\u043e \u0438 \u0432 \u043b\u043e\u0431 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u0432\u0435\u0441\u0430 20% \/ 80%                 if num &gt; 20 then                     ngx.var.upstream_id = 1                     ngx.ctx.upid = ngx.var.upstream_id                 else                     ngx.var.upstream_id = 2                     ngx.ctx.upid = ngx.var.upstream_id                 end                 -- ID \u0437\u0430\u043f\u043e\u043c\u0438\u043d\u0430\u0435\u043c \u0432 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439 nginx-\u0430 &quot;upstream_id&quot; \u0438 \u0432 &quot;upid&quot; \u0442\u0430\u0431\u043b\u0438\u0446\u044b ngx.ctx \u043c\u043e\u0434\u0443\u043b\u044f lua, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0432 \u0440\u0430\u043c\u043a\u0430\u0445 \u043e\u0434\u043d\u043e\u0433\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430              }     # \u043e\u0442\u0434\u0430\u0451\u043c \u043a\u043b\u0438\u0435\u043d\u0442\u0443 \u043a\u0443\u043a\u0443 &quot;upid&quot; \u0441\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\u043c \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u0433\u043e ID     # \u0432\u0440\u0435\u043c\u044f \u0436\u0438\u0437\u043d\u0438 \u044f\u0432\u043d\u043e \u043d\u0435 \u0437\u0430\u0434\u0430\u0451\u043c, \u043f\u043e\u0442\u043e\u043c\u0443 \u043e\u043d\u0430 \u0431\u0443\u0434\u0435\u0442 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u0430 \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0430 \u043e\u0434\u043d\u0443 \u0441\u0435\u0441\u0441\u0438\u044e (\u0434\u043e \u0437\u0430\u043a\u0440\u044b\u0442\u0438\u044f \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430), \u0447\u0442\u043e \u043d\u0430\u0441 \u0443\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u0442     add_header Set-Cookie &quot;upid=$upstream_id; Domain=$host; Path=\/&quot;;     }      # \u0435\u0441\u043b\u0438 \u0436\u0435 \u043a\u0443\u043a\u0430 \u0443 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u0443\u0436\u0435 \u0435\u0441\u0442\u044c, \u0442\u043e \u0437\u0430\u043f\u043e\u043c\u0438\u043d\u0430\u0435\u043c ID \u0432 ngx.ctx.upid \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430     if ($cookie_upid != &quot;&quot;) {         rewrite_by_lua_block {             ngx.ctx.upid = ngx.var.cookie_upid         }     }      # \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u043c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u043d\u0430 \u0431\u043b\u043e\u043a upstream-\u043e\u0432     proxy_pass http:\/\/ab_test;   } }<\/code><\/pre>\n<p>  \u0411\u043b\u043e\u043a upstream, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f lua \u0437\u0430\u043c\u0435\u043d\u044f\u0435\u0442 \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u0443\u044e \u043b\u043e\u0433\u0438\u043a\u0443 nginx.<\/p>\n<p>  \u041a\u043e\u0434 \u0441 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u043c\u0438:  <\/p>\n<pre><code class=\"lua\">upstream ab_test {   # \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0430, \u0447\u0442\u043e\u0431\u044b nginx \u043d\u0435 \u0440\u0443\u0433\u0430\u043b\u0441\u044f. \u0412 \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c\u0435 \u043d\u0435 \u0443\u0447\u0430\u0441\u0442\u0432\u0443\u0435\u0442   server 127.0.0.1:8001;      balancer_by_lua_block {         local balancer = require &quot;ngx.balancer&quot;          -- \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435         -- port \u0432\u044b\u0431\u0438\u0440\u0430\u0435\u043c \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438, \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0437\u0430\u043f\u043e\u043c\u043d\u0435\u043d\u043d\u043e\u0433\u043e ID \u0431\u044d\u043a\u0435\u043d\u0434\u0430         local host = &quot;127.0.0.1&quot;         local port = 8000 + ngx.ctx.upid          -- \u0437\u0430\u0434\u0430\u0451\u043c \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0439 upstream \u0438 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c \u043a\u043e\u0434 \u0432\u043e\u0437\u0432\u0440\u0430\u0442\u0430         local ok, err = balancer.set_current_peer(host, port)             if not ok then                 ngx.log(ngx.ERR, &quot;failed to set the current peer: &quot;, err)                 return ngx.exit(500)             end         -- \u0432 \u043e\u0431\u0449\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043d\u0430\u0434\u043e, \u043a\u043e\u043d\u0435\u0447\u043d\u043e \u0436\u0435, \u0438\u0441\u043a\u0430\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0439 \u0431\u044d\u043a\u0435\u043d\u0434, \u043d\u043e \u043d\u0430\u043c \u043d\u0435 \u043a \u0447\u0435\u043c\u0443     } }<\/code><\/pre>\n<p>  \u041d\u0443 \u0438 \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0439 \u0431\u044d\u043a\u0435\u043d\u0434, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432 \u0438\u0442\u043e\u0433\u0435 \u043f\u0440\u0438\u0434\u0443\u0442 \u043a\u043b\u0438\u0435\u043d\u0442\u044b.<\/p>\n<p>  \u043a\u043e\u0434 \u0431\u0435\u0437 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0435\u0432:  <\/p>\n<pre><code class=\"nginx\">server {   listen        127.0.0.1:8001;   server_name   test.domain.local;    location \/ {     root                \/var\/www\/html;     index               index.html;   } }  server {   listen        127.0.0.1:8002;   server_name   test.domain.local;    location \/ {     root                \/var\/www\/html;     index               index2.html;   } }<\/code><\/pre>\n<p>  \u041f\u0440\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0435 nginx-a \u0441 \u044d\u0442\u043e\u0439 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0435\u0439 \u0432 \u043b\u043e\u0433\u0438 \u0441\u0432\u0430\u043b\u0438\u0442\u0441\u044f \u043f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0435\u043d\u0438\u0435:  <\/p>\n<pre><code class=\"bash\">use of lua-resty-core with LuaJIT 2.0 is not recommended; use LuaJIT 2.1+ instead while connecting to upstream<\/code><\/pre>\n<\/blockquote>\n<p>  <\/div>\n<\/p><\/div>\n<h4>2\u0413\u0438\u0441 (<a href=\"https:\/\/habr.com\/ru\/company\/2gis\/blog\/199504\/\">\u043f\u043e\u0441\u0442<\/a>)<\/h4>\n<p>  <\/p>\n<blockquote><p>\u042d\u0442\u0443 \u0447\u0430\u0441\u0442\u044c \u043f\u0440\u0438\u0434\u0443\u043c\u0430\u043b \u0438 \u0441\u0434\u0435\u043b\u0430\u043b \u043d\u0430\u0448 \u043a\u043e\u043b\u043b\u0435\u0433\u0430 AotD. \u0415\u0441\u0442\u044c \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u043a\u0430\u0440\u0442\u0438\u043d\u043e\u043a. \u0418\u0445 \u043d\u0430\u0434\u043e \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c, \u043f\u0440\u0438\u0447\u0435\u043c \u0436\u0435\u043b\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u044c \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, resize. \u041a\u0430\u0440\u0442\u0438\u043d\u043a\u0438 \u043c\u044b \u0445\u0440\u0430\u043d\u0438\u043c \u0432 ceph, \u044d\u0442\u043e \u0430\u043d\u0430\u043b\u043e\u0433 Amazon S3. \u0414\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043a\u0430\u0440\u0442\u0438\u043d\u043e\u043a \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f ImageMagick. \u041d\u0430 \u0440\u0435\u0441\u0430\u0439\u0437\u0435\u0440\u0435 \u0435\u0441\u0442\u044c \u043a\u0430\u0442\u0430\u043b\u043e\u0433 \u0441 \u043a\u044d\u0448\u0435\u043c, \u0442\u0443\u0434\u0430 \u0441\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u043d\u043d\u044b\u0435 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438.<br \/>  \u041f\u0430\u0440\u0441\u0438\u043c \u0437\u0430\u043f\u0440\u043e\u0441 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0443, \u043d\u0443\u0436\u043d\u043e\u0435 \u0435\u043c\u0443 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0438 \u0438\u0434\u0435\u043c \u0432 ceph, \u0437\u0430\u0442\u0435\u043c \u043d\u0430 \u043b\u0435\u0442\u0443 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c \u0438 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c.<\/p><\/blockquote>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">serve_image.lua<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"lua\">require &quot;config&quot; local function return_not_found(msg)     ngx.status = ngx.HTTP_NOT_FOUND     if msg then         ngx.header[&quot;X-Message&quot;] = msg     end     ngx.exit(0) end  local name, size, ext = ngx.var.name, ngx.var.size, ngx.var.ext if not size or size == '' then     return_not_found() end if not image_scales[size] then     return_not_found('Unexpected image scale') end  local cache_dir =  static_storage_path .. '\/' .. ngx.var.first .. '\/' .. ngx.var.second .. '\/' local original_fname = cache_dir .. name .. ext local dest_fname = cache_dir .. name .. size .. ext  -- make sure the file exists local file = io.open(original_fname) if not file then     -- download file contents from ceph     ngx.req.read_body()     local data = ngx.location.capture(&quot;\/ceph_loader&quot;, {vars = { name = name .. ext }})     if data.status == ngx.HTTP_OK and data.body:len()&gt;0 then         os.execute( &quot;mkdir -p &quot; .. cache_dir )         local original = io.open(original_fname, &quot;w&quot;)         original:write(data.body)         original:close()     else         return_not_found('Original returned ' .. data.status)     end end                                                                                                                                                                                                                                   local magick = require(&quot;imagick&quot;)                                                                                                                                                                                                  magick.thumb(original_fname, image_scales[size], dest_fname)                                                                                                                                                                      ngx.exec(&quot;@after_resize&quot;)<\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  \u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u0431\u0438\u043d\u0434\u0438\u043d\u0433 imagic.lua. \u0414\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d LuaJIT.  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">nginx_partial_resizer.conf.template<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"nginx\"># Old images location ~ ^\/(small|small_wide|medium|big|mobile|scaled|original|iphone_(preview|retina_preview|big|retina_big|small|retina_small))_ {     rewrite \/([^\/]+)$ \/__CEPH_BUCKET__\/$1 break;     proxy_pass __UPSTREAM__; } # Try get image from ceph, then from local cache, then from scaled by lua original # If image test.png is original, when user wants test_30x30.png: # 1) Try get it from ceph, if not exists # 2) Try get it from \/cache\/t\/es\/test_30x30.ong, if not exists # 3) Resize original test.png and put it in \/cache\/t\/es\/test_30x30.ong location ~ ^\/(?&lt;name&gt;(?&lt;first&gt;.)(?&lt;second&gt;..)[^_]+)((?&lt;size&gt;_[^.]+)|)(?&lt;ext&gt;\\.[a-zA-Z]*)$ {     proxy_intercept_errors on;     rewrite \/([^\/]+)$ \/__CEPH_BUCKET__\/$1 break;     proxy_pass __UPSTREAM__;     error_page 404 403 = @local; } # Helper failover location for upper command cause you can't write # try_files __UPSTREAM__ \/cache\/$uri @resizer =404; location @local {     try_files \/cache\/$first\/$second\/$name$size$ext @resize; }  # If scaled file not found in local cache resize it with lua magic! location @resize { #    lua_code_cache off;     content_by_lua_file &quot;__APP_DIR__\/lua\/serve_image.lua&quot;; }  # serve scaled file, invoked in @resizer serve_image.lua location @after_resize {     try_files \/cache\/$first\/$second\/$name$size$ext =404; }  # used in @resizer serve_image.lua to download original image # $name contains original image file name location =\/ceph_loader {     internal;     rewrite ^(.+)$ \/__CEPH_BUCKET__\/$name break;     proxy_set_header Cache-Control no-cache;     proxy_set_header If-Modified-Since &quot;&quot;;     proxy_set_header If-None-Match &quot;&quot;;     proxy_pass __UPSTREAM__; }  location =\/favicon.ico {     return 404; }  location =\/robots.txt {}<\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  Firewall \u0434\u043b\u044f API. \u0412\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u0430, \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u043a\u043b\u0438\u0435\u043d\u0442\u0430, \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044c rps \u0438 \u0448\u043b\u0430\u0433\u0431\u0430\u0443\u043c \u0434\u043b\u044f \u0442\u0435\u0445, \u043a\u0442\u043e \u043d\u0430\u043c \u043d\u0435 \u043d\u0443\u0436\u0435\u043d.  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">firewall.lua<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"lua\">module(..., package.seeall); local function ban(type, element)     CStorage.banPermanent:set(type .. '__' .. element, 1);     ngx.location.capture('\/postgres_ban', { ['vars'] = { ['type'] = type, ['value'] = element} }); end local function checkBanned(apiKey)     -- init search criteria     local searchCriteria = {};     searchCriteria['key'] = apiKey;     if ngx.var.remote_addr then         searchCriteria['ip'] = ngx.var.remote_addr;     end;     -- search in ban lists     for type, item in pairs(searchCriteria) do         local storageKey = type .. '__' .. item;         if CStorage.banPermanent:get(storageKey) then             ngx.exit(444);         elseif CStorage.banTmp:get(storageKey) then             -- calculate rps and check is our client still bad boy 8-)             local rps = CStorage.RPS:incr(storageKey, 1);             if not(rps) then                 CStorage.RPS:set(storageKey, 1, 1);                 rps=1;             end;             if rps then                 if rps &gt; config.app_params['ban_params']['rps_for_ip_to_permanent_ban'] then                     CStorage.RPS:delete(storageKey);                     ban(type, item);                     ngx.exit(444);                 elseif config.app_params['ban_params']['rps_for_ip_to_tmp_ban'] &gt; 0 and rps == config.app_params['ban_params']['rps_for_ip_to_tmp_ban'] then                     local attemptsCount = CStorage.banTmp:incr(storageKey, 1) - 1;                     if attemptsCount &gt; config.app_params['ban_params']['tmp_ban']['max_attempt_to_exceed_rps'] then                         -- permanent ban                         CStorage.banTmp:delete(storageKey);                         ban(type, item);                     end;                 end;             end;             ngx.exit(444);         end;     end; end;  local function checkTemporaryBlocked(apiKey)     local blockedData = CStorage.tmpBlockedDemoKeys:get(apiKey);     if blockedData then         --storage.tmpBlockedDemoKeys:incr(apiKey, 1); -- think about it.         return CApiException.throw('tmpDemoBlocked');     end; end;  local function checkRPS(apiKey)     local rps = nil;     -- check rps for IP and ban it if it's needed     if ngx.var.remote_addr then         local ip = 'ip__' .. tostring(ngx.var.remote_addr);         rps = CStorage.RPS:incr(ip, 1);         if not(rps) then             CStorage.RPS:set(ip, 1, 1);             rps = 1;         end;         if rps &gt; config.app_params['ban_params']['rps_for_ip_to_permanent_ban'] then             ban('ip', tostring(ngx.var.remote_addr));             ngx.exit(444);         elseif config.app_params['ban_params']['rps_for_ip_to_tmp_ban'] &gt; 0 and rps &gt; config.app_params['ban_params']['rps_for_ip_to_tmp_ban'] then             CStorage.banTmp:set(ip, 1, config.app_params['ban_params']['tmp_ban']['time']);             ngx.exit(444);         end;     end;      local apiKey_key_storage = 'key_' .. apiKey['key'];     -- check rps for key     rps = CStorage.RPS:incr(apiKey_key_storage, 1);     if not(rps) then         CStorage.RPS:set(apiKey_key_storage, 1, 1);         rps = 1;     end;     if apiKey['max_rps'] and rps &gt; tonumber(apiKey['max_rps']) then         if apiKey['mode'] == 'demo' then             CApiKey.blockTemporary(apiKey['key']);             return CApiException.throw('tmpDemoBlocked');         else             CApiKey.block(apiKey['key']);             return CApiException.throw('blocked');         end;     end;      -- similar check requests per period (RPP) for key     if apiKey['max_request_count_per_period'] and apiKey['period_length'] then         local rpp = CStorage.RPP:incr(apiKey_key_storage, 1);         if not(rpp) then             CStorage.RPP:set(apiKey_key_storage, 1, tonumber(apiKey['period_length']));             rpp = 1;         end;          if rpp &gt; tonumber(apiKey['max_request_count_per_period']) then             if apiKey['mode'] == 'demo' then                 CApiKey.blockTemporary(apiKey['key']);                 return CApiException.throw('tmpDemoBlocked');             else                 CApiKey.block(apiKey['key']);                 return CApiException.throw('blocked');             end;         end;     end; end;  function run()     local apiKey = ngx.ctx.REQUEST['key'];     if not(apiKey) then         return CApiException.throw('unauthorized');     end;     apiKey = tostring(apiKey)     -- check permanent and temporary banned     checkBanned(apiKey);     -- check api key     apiKey = CApiKey.getData(apiKey);      if not(apiKey) then         return CApiException.throw('forbidden');     end;     apiKey = JSON:decode(apiKey);     if not(apiKey['is_active']) then         return CApiException.throw('blocked');     end;      apiKey['key'] = tostring(apiKey['key']);     -- check is key in tmp blocked list     if apiKey['mode'] == 'demo' then         checkTemporaryBlocked(apiKey['key']);     end;      -- check requests count per second and per period     checkRPS(apiKey);     -- set apiKey's json to global parameter; in index.lua we send it through nginx to php application     ngx.ctx.GLOBAL['api_key'] = JSON:encode(apiKey); end;<\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">validator.lua<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"lua\">module(..., package.seeall);  local function checkApiVersion()     local apiVersion = '';     if not (ngx.ctx.REQUEST['version']) then         local nginx_request = tostring(ngx.var.uri);         local version = nginx_request:sub(2,4);         if tonumber(version:sub(1,1)) and tonumber(version:sub(3,3)) then             apiVersion = version;         else             return CApiException.throw('versionIsRequired');         end;     else         apiVersion = ngx.ctx.REQUEST['version'];     end;      local isSupported = false;     for i, version in pairs(config.app_params['supported_api_version']) do         if apiVersion == version then             isSupported = true;         end;     end;      if not (isSupported) then         CApiException.throw('unsupportedVersion');     end;      ngx.ctx.GLOBAL['api_version'] = apiVersion; end;  local function checkKey()     if not (ngx.ctx.REQUEST['key']) then         CApiException.throw('unauthorized');     end; end;  function run()     checkApiVersion();     checkKey(); end;<\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">apikey.lua<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"lua\">module ( ..., package.seeall )  function init()     if not(ngx.ctx.GLOBAL['CApiKey']) then         ngx.ctx.GLOBAL['CApiKey'] = {};     end end;  function flush()     CStorage.apiKey:flush_all();     CStorage.apiKey:flush_expired(); end;  function load()     local dbError = nil;     local dbData = ngx.location.capture('\/postgres_get_keys');     dbData = dbData.body;     dbData, dbError = rdsParser.parse(dbData);     if dbData ~= nil then         local rows = dbData.resultset         if rows then             for i, row in ipairs(rows) do                 local cacheKeyData = {};                 for col, val in pairs(row) do                     if val ~= rdsParser.null then                         cacheKeyData[col] = val;                     else                         cacheKeyData[col] = nil;                     end                 end                 CStorage.apiKey:set(tostring(cacheKeyData['key']),JSON:encode(cacheKeyData));             end;         end;     end; end;  function checkNotEmpty()     if not(ngx.ctx.GLOBAL['CApiKey']['loaded']) then         local cnt = CHelper.tablelength(CStorage.apiKey:get_keys(1));         if cnt == 0 then             load();         end;         ngx.ctx.GLOBAL['CApiKey']['loaded'] = 1;     end; end;  function getData(key)     checkNotEmpty();     return CStorage.apiKey:get(key); end;  function getStatus(key)         key = getData(key);         local result = '';         if key ~= nil then             key = JSON:decode(key);             if key['is_active'] ~= nil and  key['is_active'] == true then                 result = 'allowed';             else                 result = 'blocked';             end;         else             result = 'forbidden';         end;         return result; end;  function blockTemporary(apiKey)     apiKey = tostring(apiKey);     local isset = getData(apiKey);     if isset then         CStorage.tmpBlockedDemoKeys:set(apiKey, 1, config.app_params['ban_params']['time_demo_apikey_block_tmp']);     end; end;  function block(apiKey)     apiKey = tostring(apiKey);     local keyData = getData(apiKey);     if keyData then         ngx.location.capture('\/redis_get', { ['vars'] = { ['key'] = apiKey } });         keyData['is_active'] = false;         CStorage.apiKey:set(apiKey,JSON:encode(cacheKeyData));     end; end;<\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">storages.lua<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"lua\">module ( ..., package.seeall )  apiKey = ngx.shared.apiKey; RPS = ngx.shared.RPS; RPP = ngx.shared.RPP; banPermanent = ngx.shared.banPermanent; banTmp = ngx.shared.banTmp; tmpBlockedDemoKeys = ngx.shared.tmpBlockedDemoKeys;<\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<h4>\u0411\u043e\u043d\u0443\u0441! \u041f\u0440\u0438\u043c\u0435\u0440\u044b \u0431\u0435\u0437 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f Lua \u0432\u043e\u043e\u0431\u0449\u0435.<\/h4>\n<p>  \u0422\u043e\u043b\u044c\u043a\u043e \u043a\u043e\u043d\u0444\u0438\u0433\u0438, \u0442\u043e\u043b\u044c\u043a\u043e \u0445\u0430\u0440\u0434\u043a\u043e\u0440<\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">force no-www<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"nginx\">server {     listen 80;     server_name example.org; }  server {     listen 80;     server_name www.example.org;     return 301 $scheme:\/\/example.org$request_uri; }<\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">force https<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"nginx\">server {     listen 80;     return 301 https:\/\/$host$request_uri; }  server {     listen 443 ssl;      # let the browsers know that we only accept HTTPS     add_header Strict-Transport-Security max-age=2592000;      ... }<\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">\u0420\u0435\u0434\u0438\u0440\u0435\u043a\u0442 \u043d\u0430 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0439 \u043f\u0443\u0442\u044c \u0432 URI<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"nginx\">location \/old-site {     rewrite ^\/old-site\/(.*) http:\/\/example.org\/new-site\/$1 permanent; }<\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">\u041a\u044d\u0448 \u0444\u0430\u0439\u043b\u043e\u0432<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"nginx\">open_file_cache max=1000 inactive=20s; open_file_cache_valid 30s; open_file_cache_min_uses 2; open_file_cache_errors on;<\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">\u041a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0441 Upstream<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"nginx\">upstream backend {     server 127.0.0.1:8080;     keepalive 32; }  server {     ...     location \/api\/ {         proxy_pass http:\/\/backend;         proxy_http_version 1.1;         proxy_set_header Connection &quot;&quot;;     } }<\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  <a href=\"https:\/\/gist.github.com\/karmi\/b0a9b4c111ed3023a52d\">\u041d\u0430\u043f\u043e\u0441\u043b\u0435\u0434\u043e\u043a, \u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u0441\u043f\u0438\u0441\u043e\u043a \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0445 \u0448\u0430\u0431\u043b\u043e\u043d\u043e\u0432 \u0441 Lua \u0438 \u0431\u0435\u0437, \u0441 \u0440\u0430\u0437\u043d\u043e\u0439 \u0441\u0442\u0435\u043f\u0435\u043d\u044c\u044e \u0441\u043b\u043e\u0436\u043d\u043e\u0441\u0442\u0438<\/a><\/p>\n<h2>\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/h2>\n<p>  Lua \u0432 Nginx \u0432 \u043e\u0431\u0449\u0435\u043c \u0438 OpenResty \u0432 \u0447\u0430\u0441\u0442\u043d\u043e\u0441\u0442\u0438 \u0433\u043e\u0440\u0430\u0437\u0434\u043e \u0431\u044b\u0441\u0442\u0440\u0435\u0435 \u0438 \u043b\u0435\u0433\u043a\u043e\u0432\u0435\u0441\u043d\u0435\u0435 php. \u041e\u043d\u0438 \u043f\u043e\u043c\u043e\u0433\u0430\u044e\u0442 \u0440\u0430\u0441\u0448\u0438\u0440\u0438\u0442\u044c \u0431\u0430\u0437\u043e\u0432\u044b\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b Nginx, \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0435\u0433\u043e \u0433\u0438\u0431\u0447\u0435, \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0432 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0438 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0442\u043e\u043d\u043a\u043e\u0439 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438. OpenResty \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0432 \u043f\u0440\u043e\u0434\u0435 \u043e\u0433\u0440\u043e\u043c\u043d\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0439, \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u044f \u0435\u043c\u0443 \u0431\u043e\u0433\u0430\u0442\u0443\u044e \u044d\u043a\u043e\u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u0438 \u0441\u0438\u043b\u044c\u043d\u0443\u044e \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0443 \u043a\u043e\u043c\u044c\u044e\u043d\u0438\u0442\u0438. \u041f\u043e\u043b\u0435 \u0434\u043b\u044f \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u043e\u0432 \u0441 Lua \u043f\u043e\u0447\u0442\u0438 \u043d\u0435 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u043e, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043e\u043d \u043c\u043e\u0436\u0435\u0442 \u043f\u0440\u0438\u0433\u043e\u0434\u0438\u0442\u044c\u0441\u044f \u0432 \u0441\u0430\u043c\u044b\u0445 \u043d\u0435\u043e\u0436\u0438\u0434\u0430\u043d\u043d\u044b\u0445 \u043c\u0435\u0441\u0442\u0430\u0445. \u0415\u0441\u043b\u0438 \u0432\u044b \u0435\u0449\u0435 \u043d\u0435 \u043f\u0440\u043e\u0431\u043e\u0432\u0430\u043b\u0438 Lua-in-Nginx, \u0441\u0430\u043c\u043e\u0435 \u0432\u0440\u0435\u043c\u044f \u0438\u0437\u0443\u0447\u0438\u0442\u044c \u044d\u0442\u0443 \u0442\u0435\u043c\u0443 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435.<\/p>\n<hr>\n<p>  <\/p>\n<h4>\u041d\u0430 \u043f\u0440\u0430\u0432\u0430\u0445 \u0440\u0435\u043a\u043b\u0430\u043c\u044b<\/h4>\n<p>  \u041d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c \u0441\u0435\u0440\u0432\u0435\u0440 \u0434\u043b\u044f \u0440\u0430\u0437\u043c\u0435\u0449\u0435\u043d\u0438\u044f \u0441\u0430\u0439\u0442\u0430? <a href=\"https:\/\/vdsina.ru\/pricing?partner=habr17\">\u041d\u0430\u0448\u0430 \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u044f<\/a> \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u0435\u0442 \u043d\u0430\u0434\u0451\u0436\u043d\u044b\u0435 \u0441\u0435\u0440\u0432\u0435\u0440\u044b \u0441 \u043f\u043e\u0441\u0443\u0442\u043e\u0447\u043d\u043e\u0439 \u0438\u043b\u0438 \u0435\u0434\u0438\u043d\u043e\u0440\u0430\u0437\u043e\u0432\u043e\u0439 \u043e\u043f\u043b\u0430\u0442\u043e\u0439, \u043a\u0430\u0436\u0434\u044b\u0439 \u0441\u0435\u0440\u0432\u0435\u0440 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0451\u043d \u043a \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442-\u043a\u0430\u043d\u0430\u043b\u0443 \u0432 500 \u041c\u0435\u0433\u0430\u0431\u0438\u0442 \u0438 \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u043e \u0437\u0430\u0449\u0438\u0449\u0451\u043d \u043e\u0442 DDoS-\u0430\u0442\u0430\u043a!<\/p>\n<p>  <a href=\"https:\/\/vdsina.ru\/eternal-server?partner=habr17\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/9fa\/cf4\/a34\/9facf4a348ef01048b4eb5e16ae66daa.png\"><\/a><\/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\/company\/vdsina\/blog\/504308\/\"> https:\/\/habr.com\/ru\/company\/vdsina\/blog\/504308\/<\/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\/company\/vdsina\/blog\/504308\/\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/3d\/1p\/2x\/3d1p2x0yrvkljpmvhyvrgjzlsre.jpeg\"><\/p>\n<p>  Nginx \u2014 \u0432\u0435\u043b\u0438\u043a\u043e\u043b\u0435\u043f\u043d\u044b\u0439 \u0432\u0435\u0431-\u0441\u0435\u0440\u0432\u0435\u0440. \u0412\u0441\u0435 \u043c\u044b \u043f\u0440\u0438\u0432\u044b\u043a\u043b\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0435\u0433\u043e \u0432 \u0441\u0432\u044f\u0437\u043a\u0435 \u0441 \u0431\u0435\u043a\u0435\u043d\u0434\u043e\u043c\u0430\u043c\u0438 \u043d\u0430 \u0440\u0430\u0437\u043d\u044b\u0445 \u044f\u0437\u044b\u043a\u0430\u0445 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f. \u041d\u043e \u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043c\u043e\u0436\u043d\u043e \u043f\u0438\u0441\u0430\u0442\u044c \u043f\u0440\u043e\u0441\u0442\u044b\u0435 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u044b \u043f\u0440\u044f\u043c\u043e \u0432\u043d\u0443\u0442\u0440\u0438 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u043e\u0433\u043e \u0444\u0430\u0439\u043b\u0430 Nginx. \u042d\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u043b\u044f \u0431\u0430\u043b\u0430\u043d\u0441\u0438\u0440\u043e\u0432\u043a\u0438, \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u043f\u0440\u043e\u0441\u0442\u044b\u0445 API \u0438 \u0434\u0430\u0436\u0435 \u043e\u0442\u0434\u0430\u0432\u0430\u0442\u044c \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u043f\u0440\u044f\u043c\u043e \u0438\u0437 \u043a\u043e\u043d\u0444\u0438\u0433\u0430. <\/p>\n<p>  \u0412 \u0441\u0442\u0430\u0442\u044c\u0435 \u043c\u044b \u0440\u0430\u0437\u0431\u0435\u0440\u0435\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u044b \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c \u0432 \u043a\u043e\u043d\u0444\u0438\u0433\u0435 nginx.  <\/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-304539","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/304539","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=304539"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/304539\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=304539"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=304539"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=304539"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}