{"id":282,"date":"2013-08-29T12:00:42","date_gmt":"2013-08-29T12:00:42","guid":{"rendered":"https:\/\/reviewsignal.com\/blog\/?p=282"},"modified":"2013-08-28T20:11:38","modified_gmt":"2013-08-28T20:11:38","slug":"reverse-proxy-and-cache-server-with-nginx","status":"publish","type":"post","link":"https:\/\/reviewsignal.com\/blog\/2013\/08\/29\/reverse-proxy-and-cache-server-with-nginx\/","title":{"rendered":"Reverse Proxy and Cache Server with Nginx"},"content":{"rendered":"<p>This is one of the ways I improve performance here at <a href=\"https:\/\/reviewsignal.com\">Review Signal<\/a>. I run an nginx reverse proxy and cache system in front of the apache server. Apache can be slow and doesn't have a built in caching system for a lot of the static content we serve. So I put Nginx in front to cache and serve all the content it can directly from memory. This improves the performance of my servers and users get their content faster. It also can help when there is a high load. If you want to see how it performs, I've included a screenshot from <a href=\"http:\/\/blitz.io\/bhmeMI5LF0Pc8neVAZEL2wv\">Blitz.io<\/a> at the bottom showing how well Review Signal performs with 500 concurrent users.<\/p>\n<p><strong>The Nginx config (http, server):<\/strong><\/p>\n\n<div class=\"wp_syntax\"><table><tr><td class=\"code\"><pre class=\"bash\" style=\"font-family:monospace;\">http <span style=\"color: #7a0874; font-weight: bold;\">&#123;<\/span>\n  proxy_redirect off;\n  proxy_set_header Host <span style=\"color: #007800;\">$host<\/span>;\n  proxy_set_header X-Real-IP <span style=\"color: #007800;\">$remote_addr<\/span>;\n  proxy_set_header X-Forwarded-For <span style=\"color: #007800;\">$proxy_add_x_forwarded_for<\/span>;\n&nbsp;\n  <span style=\"color: #666666; font-style: italic;\"># caching options<\/span>\n  proxy_cache_path <span style=\"color: #000000; font-weight: bold;\">\/<\/span>var<span style=\"color: #000000; font-weight: bold;\">\/<\/span>cache<span style=\"color: #000000; font-weight: bold;\">\/<\/span>nginx <span style=\"color: #007800;\">levels<\/span>=<span style=\"color: #000000;\">1<\/span>:<span style=\"color: #000000;\">2<\/span> <span style=\"color: #007800;\">keys_zone<\/span>=my-cache:8m \n<span style=\"color: #007800;\">max_size<\/span>=1000m <span style=\"color: #007800;\">inactive<\/span>=600m;\n  proxy_temp_path <span style=\"color: #000000; font-weight: bold;\">\/<\/span>var<span style=\"color: #000000; font-weight: bold;\">\/<\/span>cache<span style=\"color: #000000; font-weight: bold;\">\/<\/span>tmp;\n&nbsp;\nserver <span style=\"color: #7a0874; font-weight: bold;\">&#123;<\/span>\n  listen <span style=\"color: #000000;\">80<\/span>;\n  server_name subdomain.example.com;\n  access_log on;\n  error_log on;\n&nbsp;\n  location <span style=\"color: #000000; font-weight: bold;\">\/<\/span><span style=\"color: #7a0874; font-weight: bold;\">&#123;<\/span>\n    proxy_pass http:<span style=\"color: #000000; font-weight: bold;\">\/\/<\/span>localhost:<span style=\"color: #000000;\">3000<\/span><span style=\"color: #000000; font-weight: bold;\">\/<\/span>subdomain;\n  <span style=\"color: #7a0874; font-weight: bold;\">&#125;<\/span>\n<span style=\"color: #7a0874; font-weight: bold;\">&#125;<\/span>\n&nbsp;\nserver <span style=\"color: #7a0874; font-weight: bold;\">&#123;<\/span>\n  listen <span style=\"color: #000000;\">80<\/span>;\n  server_name example.com;\n  access_log on;\n  error_log on;\n&nbsp;\n  location <span style=\"color: #000000; font-weight: bold;\">\/<\/span> <span style=\"color: #7a0874; font-weight: bold;\">&#123;<\/span> \n    proxy_pass http:<span style=\"color: #000000; font-weight: bold;\">\/\/<\/span>localhost:<span style=\"color: #000000;\">3000<\/span><span style=\"color: #000000; font-weight: bold;\">\/<\/span>; \n    proxy_cache my-cache;\n    proxy_cache_valid <span style=\"color: #000000;\">200<\/span> <span style=\"color: #000000;\">302<\/span> 60m;\n    proxy_cache_valid <span style=\"color: #000000;\">404<\/span> 1m;\n  <span style=\"color: #7a0874; font-weight: bold;\">&#125;<\/span>\n <span style=\"color: #7a0874; font-weight: bold;\">&#125;<\/span>\n<span style=\"color: #7a0874; font-weight: bold;\">&#125;<\/span><\/pre><\/td><\/tr><\/table><\/div>\n\n<p>Please note that only the relevant parts to this article are included (http and server). You definitely need to add more options to the config before http and in your http section. I didn't include those parts because they can vary a huge amount. See the Nginx <a href=\"http:\/\/nginx.org\/en\/docs\/ngx_core_module.html\">documentation<\/a> \u00a0for more details and example configurations.<\/p>\n<p><strong>Configuration Walkthrough:<\/strong><\/p>\n\n<div class=\"wp_syntax\"><table><tr><td class=\"code\"><pre class=\"bash\" style=\"font-family:monospace;\">  proxy_redirect off;\n  proxy_set_header Host <span style=\"color: #007800;\">$host<\/span>;\n  proxy_set_header X-Real-IP <span style=\"color: #007800;\">$remote_addr<\/span>;\n  proxy_set_header X-Forwarded-For <span style=\"color: #007800;\">$proxy_add_x_forwarded_for<\/span>;<\/pre><\/td><\/tr><\/table><\/div>\n\n<p>proxy_redirect off tells the server we aren't redirecting content. We actually do that later with proxy_pass. The headers we set allow you to see proper header information on the server you are proxying to. Without X-Real-IP\/X-Forwarded-For the server will simply see your reverse proxy server's IP address.<\/p>\n\n<div class=\"wp_syntax\"><table><tr><td class=\"code\"><pre class=\"bash\" style=\"font-family:monospace;\">  proxy_cache_path <span style=\"color: #000000; font-weight: bold;\">\/<\/span>var<span style=\"color: #000000; font-weight: bold;\">\/<\/span>cache<span style=\"color: #000000; font-weight: bold;\">\/<\/span>nginx <span style=\"color: #007800;\">levels<\/span>=<span style=\"color: #000000;\">1<\/span>:<span style=\"color: #000000;\">2<\/span> <span style=\"color: #007800;\">keys_zone<\/span>=my-cache:8m \n<span style=\"color: #007800;\">max_size<\/span>=1000m <span style=\"color: #007800;\">inactive<\/span>=600m;\n  proxy_temp_path <span style=\"color: #000000; font-weight: bold;\">\/<\/span>var<span style=\"color: #000000; font-weight: bold;\">\/<\/span>cache<span style=\"color: #000000; font-weight: bold;\">\/<\/span>tmp;<\/pre><\/td><\/tr><\/table><\/div>\n\n<p>The first line tells nginx where to save cache data (path), the structure of the cache data (levels), names your cache and it's size (keys_zone), max cache size (max_size) and how long before cached data is expired (inactive). The second line tells nginx where to save temporary data which it uses in building the cache.<\/p>\n\n<div class=\"wp_syntax\"><table><tr><td class=\"code\"><pre class=\"bash\" style=\"font-family:monospace;\">server <span style=\"color: #7a0874; font-weight: bold;\">&#123;<\/span>\n  listen <span style=\"color: #000000;\">80<\/span>;\n  server_name subdomain.example.com;\n  access_log on;\n  error_log on;\n&nbsp;\n  location <span style=\"color: #000000; font-weight: bold;\">\/<\/span><span style=\"color: #7a0874; font-weight: bold;\">&#123;<\/span>\n    proxy_pass http:<span style=\"color: #000000; font-weight: bold;\">\/\/<\/span>localhost:<span style=\"color: #000000;\">3000<\/span><span style=\"color: #000000; font-weight: bold;\">\/<\/span>subdomain;\n  <span style=\"color: #7a0874; font-weight: bold;\">&#125;<\/span>\n<span style=\"color: #7a0874; font-weight: bold;\">&#125;<\/span><\/pre><\/td><\/tr><\/table><\/div>\n\n<p>This server code creates a reverse proxy to localhost:3000. It doesn't do any caching, it simply forwards all requests between Nginx and the localhost:3000. It is listening to subdomain.example.com and any request to it (\/) are passed to localhost:3000\/subdomain.<\/p>\n\n<div class=\"wp_syntax\"><table><tr><td class=\"code\"><pre class=\"bash\" style=\"font-family:monospace;\">server <span style=\"color: #7a0874; font-weight: bold;\">&#123;<\/span>\n  listen <span style=\"color: #000000;\">80<\/span>;\n  server_name example.com;\n  access_log on;\n  error_log on;\n&nbsp;\n  location <span style=\"color: #000000; font-weight: bold;\">\/<\/span> <span style=\"color: #7a0874; font-weight: bold;\">&#123;<\/span> \n    proxy_pass http:<span style=\"color: #000000; font-weight: bold;\">\/\/<\/span>localhost:<span style=\"color: #000000;\">3000<\/span><span style=\"color: #000000; font-weight: bold;\">\/<\/span>; \n    proxy_cache my-cache;\n    proxy_cache_valid <span style=\"color: #000000;\">200<\/span> <span style=\"color: #000000;\">302<\/span> 60m;\n    proxy_cache_valid <span style=\"color: #000000;\">404<\/span> 1m;\n  <span style=\"color: #7a0874; font-weight: bold;\">&#125;<\/span>\n <span style=\"color: #7a0874; font-weight: bold;\">&#125;<\/span><\/pre><\/td><\/tr><\/table><\/div>\n\n<p>This server is a reverse proxy and cache. We're responding to any request to example.com. It's forwarding all requests to localhost:3000. It also is creates a cache called my-cache (notice this matches the proxy_cache_path keys_zone setting). proxy_cache_valid defines what HTTP codes can be cached and for how long. So in this example 200 (OK) and 302 (FOUND) are cached for 60 minutes. 404 (NOT FOUND) is cached for 1 minute.<\/p>\n<p><strong>Conclusion<\/strong><\/p>\n<p>Setting up a reverse proxy isn't too difficult. However, it can be complicated to get it working with your application on occasion. You can empty the cache manually by deleting all the contents in the cache folder. That often helps fix issues. Nginx is fairly smart and when you pass post data, it doesn't serve cached pages, but get\/head data will be cached by default. This setup works great when you serve a lot of static content. I run it in front of almost everything here at Review Signal including our blog. It's easy enough to configure different caching levels for different parts of your application. And if you're ever in doubt, test the application directly, then just reverse proxy without caching, and then turn on caching.<\/p>\n<p>As promised, here is what Review Signal's performance looks like when rushing with <a href=\"http:\/\/blitz.io\/bhmeMI5LF0Pc8neVAZEL2wv\">Blitz.io<\/a> from 1-500 concurrent users with this nginx setup. It responds to around 300 concurrent requests without going over 100ms response time.<\/p>\n<p><a href=\"https:\/\/reviewsignal.com\/blog\/wp-content\/uploads\/2013\/08\/nginxblitzio.png\"><img loading=\"lazy\" class=\"alignnone size-medium wp-image-283\" alt=\"nginxblitzio\" src=\"https:\/\/reviewsignal.com\/blog\/wp-content\/uploads\/2013\/08\/nginxblitzio-625x452.png\" width=\"625\" height=\"452\" srcset=\"https:\/\/reviewsignal.com\/blog\/wp-content\/uploads\/2013\/08\/nginxblitzio-625x452.png 625w, https:\/\/reviewsignal.com\/blog\/wp-content\/uploads\/2013\/08\/nginxblitzio-624x451.png 624w, https:\/\/reviewsignal.com\/blog\/wp-content\/uploads\/2013\/08\/nginxblitzio.png 916w\" sizes=\"(max-width: 625px) 100vw, 625px\" \/><\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>This is one of the ways I improve performance here at Review Signal. I run an nginx reverse proxy and cache system in front of the apache server. Apache can be slow and doesn&#8217;t have a built in caching system for a lot of the static content we serve. So I put Nginx in front [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[40],"tags":[71,73,57,74,72],"_links":{"self":[{"href":"https:\/\/reviewsignal.com\/blog\/wp-json\/wp\/v2\/posts\/282"}],"collection":[{"href":"https:\/\/reviewsignal.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/reviewsignal.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/reviewsignal.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/reviewsignal.com\/blog\/wp-json\/wp\/v2\/comments?post=282"}],"version-history":[{"count":4,"href":"https:\/\/reviewsignal.com\/blog\/wp-json\/wp\/v2\/posts\/282\/revisions"}],"predecessor-version":[{"id":287,"href":"https:\/\/reviewsignal.com\/blog\/wp-json\/wp\/v2\/posts\/282\/revisions\/287"}],"wp:attachment":[{"href":"https:\/\/reviewsignal.com\/blog\/wp-json\/wp\/v2\/media?parent=282"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/reviewsignal.com\/blog\/wp-json\/wp\/v2\/categories?post=282"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/reviewsignal.com\/blog\/wp-json\/wp\/v2\/tags?post=282"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}