One place for hosting & domains

      Cache

      How to Use the Nginx FastCGI Page Cache With WordPress


      NGINX typically fetches a new copy of a web page each time a new request is made. However for complex sites such as WordPress, each new request often involves querying a database and executing PHP code. For heavily used sites, this can cause performance issues and increased latency. As a workaround, NGINX offers the FastCGI page cache, allowing it to serve a static copy of the page more quickly. This guide explains how to enable caching for a WordPress site hosted on an NGINX web server. It also demonstrates how to configure non-cachable exceptions and how to test whether caching is working.

      How Does Caching Work on NGINX?

      When NGINX receives a request for a WordPress file, it typically has to perform extra processing. The usual work flow for this type of request follows this process:

      1. NGINX receives a request for a WordPress page. It locates the page and determines if further processing is required.
      2. Most WordPress pages include PHP code, which the web server cannot directly interpret. To execute the PHP code, NGINX sends the page to the PHP-FPM (PHP FastCGI Process Manager) module. PHP-FPM is a popular and efficient implementation of the FastCGI protocol. FastCGI allows a web server to interactively interface with other programs.
      3. PHP-FPM interprets the PHP code. If necessary, it connects to a MySQL or MariaDB database and executes any SQL queries in the code.
      4. PHP-FPM converts the original page into a static HTML page and sends it back to NGINX.
      5. NGINX serves the static page to the original web client.

      Unfortunately, this takes more time than serving a static HTML page. The PHP-FPM module must always parse the PHP code and there is often an additional delay to contact the database. For low-volume WordPress sites, this is typically not a problem. But for popular sites, these delays can result in serious performance bottlenecks and unacceptably high latency.

      Caching is one way of speeding up execution and improving performance. When caching is enabled, NGINX stores the pages it receives from the PHP-FPM as static HTML web pages. The next time the page is requested, NGINX does not contact the PHP-FPM to generate a new version of the page. Instead, it retrieves the static HTML version of the page from cache and transmits it to the client. Because it is much faster to retrieve a static page from the NGINX cache, site performance usually improves.

      As an additional bonus, NGINX can serve a cached version of the page even if the database or the PHP interpreter are not reachable. This optimization improves site reliability and robustness. The cached files typically expire after a certain length of time, so users do not continue to receive stale content. Extra configuration can also be added to allow certain pages to bypass the cache.

      No additional components or applications are required to enable the NGINX cache. All cache directives are added to existing virtual host or NGINX configuration files. However, WordPress plugins can improve cache management, allowing the cache to be purged under certain circumstances, such as when a page is updated.

      Before You Begin

      1. If you have not already done so, create a Linode account and Compute Instance. See our
        Getting Started with Linode and
        Creating a Compute Instance guides.

      2. Follow our
        Setting Up and Securing a Compute Instance guide to update your system. You may also wish to set the timezone, configure your hostname, create a limited user account, and harden SSH access.

      3. Configure a LEMP stack on the server, consisting of the NGINX web server, the MariaDB database, and the PHP programming language. MySQL can be substituted in place of MariaDB. For information on how to install and configure a LEMP stack, see the Linode guide on
        installing a LEMP stack on Ubuntu 22.04.

      4. Ensure WordPress is installed and updated. To install WordPress, review the Linode guide on
        installing WordPress on Ubuntu.

      5. WordPress sites are almost always accessed using a domain name. For more information on domains and how to create a DNS record, see the
        Linode DNS Manager guide.

      Note

      This guide is written for a non-root user. Commands that require elevated privileges are prefixed with sudo. If you are not familiar with the sudo command, see the
      Users and Groups guide.

      How to Configure the LEMP Stack to Support Caching

      WordPress caching can be enabled on NGINX without installing additional components. Ensure all Linux packages are updated before proceeding.

      The FastCGI instructions require a LEMP stack including the NGINX web server rather than the more common LAMP stack. These instructions are geared towards Ubuntu 22.04 LTS, but are generally applicable to any Linux distribution using a LEMP stack.

      How to Configure The WordPress Virtual Host for Caching

      Caching is typically enabled on a site-by-site basis. Caching directives are added directly to the .conf file for the WordPress virtual host. If encryption using HTTPS is enabled, the structure of the file might be different. However, the cache directives should still be added to the server and php code blocks as shown below. To enable the NGINX cache for WordPress, follow these instructions. For more information on these directives, see the
      NGINX documentation for the HTTP FastCGI module.

      1. Edit the virtual host .conf file for the WordPress domain. If WordPress is configured according to the Linode guide, this file is found at /etc/nginx/sites-available/example.com.conf. Substitute the actual name of the WordPress domain for example.com.conf.

        sudo nano /etc/nginx/sites-available/example.com.conf
      2. At the top of the file, before the server block, add the following three directives. Some attributes can be configured to best meet the requirements of the site.

        • The fastcgi_cache_path directive specifies the location of the cache and the cache parameters.
        • The keys_zone attribute defines the wpcache cache and the size of the shared memory zone. 200m is enough space for over a million keys. In many cases, this can be set to a smaller size.
        • The max_size field indicates the maximum size of the actual cache on disk. This guide sets max_size to 10g. Feel free to choose a larger or smaller amount.
        • The inactive attribute tells NGINX when to purge data from the cache. This example uses a two-hour limit, indicated by inactive=2h. Cache contents that have not been accessed during this period are deleted.
        • The fastcgi_cache_key directive defines the key format.
        • fastcgi_ignore_headers disables the processing of certain response header fields that could adversely affect caching.

        Note

        This example only enables caching on the WordPress domain. However, the directives in this section can also be configured on a server-wide basis. To apply these instructions to the entire server, add them to the top of the /etc/nginx/nginx.conf file instead. The remaining configuration must be added to the WordPress virtual host file.

        File: /etc/nginx/sites-available/example.com.conf
        1
        2
        3
        
        fastcgi_cache_path /etc/nginx/cache levels=1:2 keys_zone=wpcache:200m max_size=10g inactive=2h use_temp_path=off;
        fastcgi_cache_key "$scheme$request_method$host$request_uri";
        fastcgi_ignore_headers Cache-Control Expires Set-Cookie;
      3. Include exceptions for any pages that must not be cached. Some examples of pages to bypass are the WordPress administration panel, cookies, session data, queries, and POST requests. When any of the following conditions are met, the temporary variable skip_cache is set to 1. Later on, this variable is used to inform NGINX not to search the cache or cache the new contents. Add the following lines inside the server block, immediately after the line beginning with index.

        File: /etc/nginx/sites-available/example.com.conf
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        
        set $skip_cache 0;
        
        if ($request_method = POST) {
            set $skip_cache 1;
        }
        if ($query_string != "") {
            set $skip_cache 1;
        }
        
        if ($request_uri ~* "/wp-admin/|/xmlrpc.php|wp-.*.php|^/feed/*|/tag/.*/feed/*|index.php|/.*sitemap.*\.(xml|xsl)") {
            set $skip_cache 1;
        }
        
        if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_no_cache|wordpress_logged_in") {
            set $skip_cache 1;
        }
      4. Optional To avoid caching requests from a specific access or test address, include the following lines. Substitute the actual addresses for testaddr1 and testaddr2.

        Note

        Adding this rule means it is not possible to test caching from these addresses.

        File: /etc/nginx/sites-available/example.com.conf
        1
        2
        3
        
        if ($remote_addr ~* "testaddr1|testaddr2") {
            set $skip_cache 1;
        }
      5. Add the next set of directives to the block beginning with location ~ \.php$ beneath any pre-existing instructions. This configuration includes the following directive:

        • The fastcgi_cache tells NGINX to enable caching. The name of the cache must match the name of the cache defined in the fastcgi_cache_path directive.
        • fastcgi_cache_valid defines the cache expiry time for specific HTTP status codes.
        • A handy NGINX attribute is the ability to deliver cached content when PHP-FPM or the database are unavailable. fastcgi_cache_use_stale error defines the conditions where NGINX should serve stale content. In many cases, this is preferable to returning an error page to clients.
        • fastcgi_cache_min_uses indicates how many times a page must be requested before it is cached. Setting this attribute to a larger value avoids caching rarely-used pages and can help manage the cache size.
        • fastcgi_cache_lock tells NGINX how to handle concurrent requests.
        • The fastcgi_cache_bypass and fastcgi_no_cache are assigned based on the value of skip_cache from the previous section. This tells NGINX not to search the cache and not to store any new content.
        • The add_header instruction is used to add a header field indicating whether the resource is taken from the cache or not. This field is handy for debug purposes, but is not strictly required in production code.
        1
        2
        3
        4
        5
        6
        7
        8
        
        fastcgi_cache wpcache;
        fastcgi_cache_valid 200 301 302 2h;
        fastcgi_cache_use_stale error timeout updating invalid_header http_500 http_503;
        fastcgi_cache_min_uses 1;
        fastcgi_cache_lock on;
        fastcgi_cache_bypass $skip_cache;
        fastcgi_no_cache $skip_cache;
        add_header X-FastCGI-Cache $upstream_cache_status;
      6. The entire file should be similar to the following example.

        File: /etc/nginx/sites-available/example.com.conf
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        46
        47
        48
        49
        
        fastcgi_cache_path /etc/nginx/cache levels=1:2 keys_zone=wpcache:200m max_size=10g inactive=2h use_temp_path=off;
        fastcgi_cache_key "$scheme$request_method$host$request_uri";
        fastcgi_ignore_headers Cache-Control Expires Set-Cookie;
        
        server {
            listen 80;
            listen [::]:80;
        
            server_name example.com www.example.com;
            root /var/www/html/example.com/public_html;
            index index.html;
            set $skip_cache 0;
        
            if ($request_method = POST) {
                set $skip_cache 1;
            }
            if ($query_string != "") {
                set $skip_cache 1;
            }
        
            if ($request_uri ~* "/wp-admin/|/xmlrpc.php|wp-.*.php|^/feed/*|/tag/.*/feed/*|index.php|/.*sitemap.*\.(xml|xsl)") {
                set $skip_cache 1;
            }
        
            if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_no_cache|wordpress_logged_in") {
                set $skip_cache 1;
            }
        
            location / {
                index index.php index.html index.htm;
                try_files $uri $uri/ =404;
            }
            location ~ \.php$ {
                fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
                include snippets/fastcgi-php.conf;
                fastcgi_cache wpcache;
                fastcgi_cache_valid 200 301 302 2h;
                fastcgi_cache_use_stale error timeout updating invalid_header http_500 http_503;
                fastcgi_cache_min_uses 1;
                fastcgi_cache_lock on;
                fastcgi_cache_bypass $skip_cache;
                fastcgi_no_cache $skip_cache;
                add_header X-FastCGI-Cache $upstream_cache_status;
            }
        
            location ~ /\.ht {
                deny all;
            }
        }
      7. Test the NGINX configuration to ensure it contains no errors.

        nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
        nginx: configuration file /etc/nginx/nginx.conf test is successful
            
      8. Restart NGINX to apply the changes.

        sudo systemctl restart nginx

      How to Test the WordPress Cache

      To test whether the WordPress cache is working correctly, use the command curl -I http://example.com, replacing example.com with the actual name of the WordPress site. Ensure you are not logged in to WordPress from this host and have not excluded the host address as a test domain address.

      1. Run the curl command and review the output. The X-FastCGI-Cache indicates MISS because the item has not been cached yet.

        curl -I http://example.com/
        HTTP/1.1 200 OK
        Server: nginx/1.18.0 (Ubuntu)
        Date: Wed, 23 Nov 2022 15:16:03 GMT
        Content-Type: text/html; charset=UTF-8
        Connection: keep-alive
        Link: <http://example.com/index.php?rest_route=/>; rel="https://api.w.org/"
        X-FastCGI-Cache: MISS
            
      2. Run the curl command again. This time, X-FastCGI-Cache now displays HIT because the item is retrieved from the NGINX cache.

        curl -I http://example.com/
        HTTP/1.1 200 OK
        Server: nginx/1.18.0 (Ubuntu)
        Date: Wed, 23 Nov 2022 15:16:08 GMT
        Content-Type: text/html; charset=UTF-8
        Connection: keep-alive
        Link: <http://example.com/index.php?rest_route=/>; rel="https://api.w.org/"
        X-FastCGI-Cache: HIT
            
      3. Run the command a few more times to confirm the cache is HIT each time.

      4. To ensure the exceptions are in effect, use curl to access the wp-admin page. This time, the X-FastCGI-Cache should indicate BYPASS.

        curl -I http://example.com/wp-admin/post.php
        Cache-Control: no-cache, must-revalidate, max-age=0
        ...
        X-FastCGI-Cache: BYPASS
            

      (Optional) How to Enable WordPress Cache Purging

      NGINX does not provide an easy way to clear the cache. While it is possible to write a purge function using NGINX commands, it is easier to use an existing WordPress plugin. The
      Nginx Helper plugin is fully compatible with WordPress and allows users to configure how and when to purge the cache. This plugin also allows users to manually clear the cache. To install and configure Nginx Helper, follow these steps.

      1. To support cache purging, install the following NGINX module.

        sudo apt install libnginx-mod-http-cache-purge
      2. Using the WordPress administration panel, install the Nginx Helper plugin. Select the Plugins option on the side panel, then select Add New.

        Select the WordPress Plugins Panel

      3. In the search box on the upper right corner of the WordPress administration panel, type NGINX Helper and hit Enter. The Nginx Helper plugin is one of the top results on the first line of the plugins. Click the Install Now button beside it to install.

        Install the Nginx Helper plugin

      4. After WordPress installs the plugin, select the Activate button to enable it.

        Activate the Nginx Helper plugin

      5. The Nginx Helper plugin requires some further configuration. From the side navigation panel, click the Settings label, then select Nginx Helper.

        Choose the Nginx Helper settings

      6. On the Nginx Helper Settings page, select Enable Purge. After this option is enabled, the WordPress administration panel displays more options. Ensure the Caching Method is set to nginx Fastcgi cache. Select the Purging Conditions according to your preferences. In most cases, the default settings are appropriate. Select the Save All Changes button to confirm and save the selections.

        Note

        Debug Options are available near the bottom of the configuration page to enable logging and timestamps for easier troubleshooting.

      7. Inside /etc/nginx/sites-available/example.com.conf, add the following lines to the server context. Add this block immediately after the location ~ \.php$ block.

        File: /etc/nginx/sites-available/example.com.conf
        1
        2
        3
        
        location ~ /purge(/.*) {
              fastcgi_cache_purge wpcache "$scheme$request_method$host$1";
        }
      8. Test the NGINX configuration and restart the web server.

        sudo nginx -t
        sudo systemctl restart nginx
      9. Update the main page for the WordPress site. Access the site from an address that is not logged in to WordPress. Despite the caching configuration, the browser displays the updated page.

      Conclusion

      The NGINX FastCGI caching mechanism improves site performance and reduces latency. The NGINX cache stores WordPress pages as static HTTP pages. This eliminates the demand to execute any PHP code or access the database. To enable caching on a WordPress site, add the configuration directly to the WordPress virtual host file. No additional components are required. The FastCGI cache directives allow users to set the cache size and expiry time and add further refinements. NGINX does not directly provide a method for purging the cache, but WordPress plugins like Nginx Helper provide this functionality.

      More Information

      You may wish to consult the following resources for additional information
      on this topic. While these are provided in the hope that they will be
      useful, please note that we cannot vouch for the accuracy or timeliness of
      externally hosted materials.



      Source link

      How To Speed Up Static Web Pages with Varnish Cache Server on Ubuntu 20.04


      The author selected the COVID-19 Relief Fund to receive a donation as part of the Write for DOnations program.

      Introduction

      Varnish is a versatile reverse HTTP proxy that caches responses from backend servers in memory so they are served quickly when requested again. It uses HTTP headers to determine whether to cache the responses to a particular request. By default, it does not cache responses with cookies because those are considered client-specific requests; however you can change this setting in the configuration file.

      Besides acting as a caching server, Varnish can be used as a:

      • Web application firewall
      • DDoS attack defender
      • Load balancer
      • Quick fix for unstable backends
      • HTTP router

      There are three locations where the HTTP cache can be saved:

      • Browser: This cache is saved on users’ browsers. It is user-specific and can be used to serve content instead of sending requests to web sites.
      • Proxy: A proxy is an intermediate server that sits between users and servers. It is usually deployed by ISPs and can be used to cache responses that will be requested by multiple users.
      • Reverse Proxy: This kind of proxy is created by the web site’s administrator and can be used to serve content from the network’s edge instead of sending requests to back end servers. This is the kind of cache you will create in this tutorial.

      Note: For more information about HTTP caching, see this tutorial on HTTP headers and caching strategies.

      In this tutorial, you will set up Varnish as a caching reverse proxy server. You’ll then test the setup with Varnish against a non-caching configuration using wrk.

      Prerequisites

      To complete this tutorial, you will need:

      Step 1 — Installing Varnish And Apache

      To start, you’ll install Apache and Varnish. First update apt-get, and then install Apache with these commands:

      • sudo apt-get update
      • sudo apt-get install apache2 -y

      You’ll see output indicating that Apache is being installed.

      After the Apache installation process is complete, install Varnish with this command:

      • sudo apt-get install varnish -y

      You’ll see output indicating that Varnish is being installed.

      Next, make sure both packages installed correctly. First, use this command to check the status of Apache:

      • sudo systemctl status apache2

      The output will look similar to this:

      Output

      root@ubuntu-s-1vcpu-2gb-fra1-01:~# sudo systemctl status apache2 ● apache2.service - The Apache HTTP Server Loaded: loaded (/lib/systemd/system/apache2.service; enabled; vendor preset: enabled) Active: active (running) since Wed 2021-08-04 18:58:39 UTC; 4min 10s ago Docs: https://httpd.apache.org/docs/2.4/ Main PID: 2279 (apache2) Tasks: 55 (limit: 2344) Memory: 5.0M CGroup: /system.slice/apache2.service ├─2279 /usr/sbin/apache2 -k start ├─2281 /usr/sbin/apache2 -k start └─2282 /usr/sbin/apache2 -k start Aug 04 18:58:39 ubuntu-s-1vcpu-2gb-fra1-01 systemd[1]: Starting The Apache HTTP Server... Aug 04 18:58:39 ubuntu-s-1vcpu-2gb-fra1-01 apachectl[2278]: AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 127.0.1.1. Set the 'ServerName' di> Aug 04 18:58:39 ubuntu-s-1vcpu-2gb-fra1-01 systemd[1]: Started The Apache HTTP Server.

      Press the Q key to exit the status command.

      Next, check the status of Varnish with this command:

      • sudo systemctl status varnish

      The output will look similar to this:

      Output

      root@ubuntu-s-1vcpu-2gb-fra1-01:~# sudo systemctl status varnish ● varnish.service - Varnish HTTP accelerator Loaded: loaded (/lib/systemd/system/varnish.service; enabled; vendor preset: enabled) Active: active (running) since Wed 2021-08-04 18:59:09 UTC; 4min 41s ago Docs: https://www.varnish-cache.org/docs/6.1/ man:varnishd Main PID: 3423 (varnishd) Tasks: 217 (limit: 2344) Memory: 10.7M CGroup: /system.slice/varnish.service ├─3423 /usr/sbin/varnishd -j unix,user=vcache -F -a :6081 -T localhost:6082 -f /etc/varnish/default.vcl -S /etc/varnish/secret -s malloc,256m └─3447 /usr/sbin/varnishd -j unix,user=vcache -F -a :6081 -T localhost:6082 -f /etc/varnish/default.vcl -S /etc/varnish/secret -s malloc,256m Aug 04 18:59:09 ubuntu-s-1vcpu-2gb-fra1-01 systemd[1]: Started Varnish HTTP accelerator. Aug 04 18:59:10 ubuntu-s-1vcpu-2gb-fra1-01 varnishd[3423]: Debug: Version: varnish-6.2.1 revision 9f8588e4ab785244e06c3446fe09bf9db5dd8753 Aug 04 18:59:10 ubuntu-s-1vcpu-2gb-fra1-01 varnishd[3423]: Version: varnish-6.2.1 revision 9f8588e4ab785244e06c3446fe09bf9db5dd8753 Aug 04 18:59:10 ubuntu-s-1vcpu-2gb-fra1-01 varnishd[3423]: Debug: Platform: Linux,5.4.0-73-generic,x86_64,-junix,-smalloc,-sdefault,-hcritbit Aug 04 18:59:10 ubuntu-s-1vcpu-2gb-fra1-01 varnishd[3423]: Platform: Linux,5.4.0-73-generic,x86_64,-junix,-smalloc,-sdefault,-hcritbit Aug 04 18:59:10 ubuntu-s-1vcpu-2gb-fra1-01 varnishd[3423]: Debug: Child (3447) Started Aug 04 18:59:10 ubuntu-s-1vcpu-2gb-fra1-01 varnishd[3423]: Child (3447) Started Aug 04 18:59:10 ubuntu-s-1vcpu-2gb-fra1-01 varnishd[3423]: Info: Child (3447) said Child starts Aug 04 18:59:10 ubuntu-s-1vcpu-2gb-fra1-01 varnishd[3423]: Child (3447) said Child starts

      If you do not see both services up and running, wait a few minutes until they are fully loaded, and keep both of them running.

      Now that you have Apache2 Varnish, installed, you’ll give Varnish something to serve, in this case Apache’s static web page.

      Step 2 — Configuring Varnish To Serve Apache’s Static Web Page

      In the previous step, you installed Varnish, and next you’ll need to configure it. By default, Varnish listens on port 6081 and connects to a local web server on port 8080. You’ll change that to serve the Apache static site from Apache server.

      First, you’ll change Varnish’s listening port to 8080. Usually you would want the listening port to be 80, but because you are running Apache and Varnish on the same server, you’ll use port 8080 for Varnish and port 80 for Apache.

      There is no configuration option to change the listening port for Varnish, so you’ll do it using the command line. You’ll create a file called customexec.conf in a new directory called varnish.service.d in /etc/systemd/system/ that will change the default ports.

      Use the mkdir command to create the new directory:

      • sudo mkdir /etc/systemd/system/varnish.service.d

      Use your favorite text editor to create a new file called customexec.conf :

      • sudo nano /etc/systemd/system/varnish.service.d/customexec.conf

      In customexec.conf, add the following content:

      /etc/systemd/system/varnish.service.d/customexec.conf file

      [Service] ExecStart= ExecStart=/usr/sbin/varnishd -j unix,user=vcache -F -a :8080 -T localhost:6082 -f /etc/varnish/default.vcl -S /etc/varnish/secret -s malloc,256m

      In this file you are changing the Service section of the Varnish configuration. First you remove the old value for the ExecStart option, and then you assign a new value for it.

      The new value specifies the binary file used to run Varnish with the following options:

      • -j: Specifies the jailing mechanism to use. Varnish jails are used to reduce the permissions for the varnish process over various platform-specific methods. Here you’re using the unix mechanism and the user vcache to limit the permissions. This is default for varnish on Ubuntu systems.

      • -F: Indicates that the server should run in the foreground, because systemd expects the main process to keep running so it can track it, and not fork a new process and die.

      • -a: This flag is used to specify the IP address and port for accepting client connections. The IP in this case is empty, which means the server will accept all IPs. The port is set to 8080.

      • -T: This flag specifies the IP address and port for management interface, in this case localhost and port 6082.

      • -f: This flag specifies the default VCL file for Varnish configuration. You will edit this file later in this tutorial to configure Varnish to connect to the Apache server.

      • -S: This flag specifies a shared secret file for authorizing access to the management interface. The /etc/varnish/secret value is the default for Varnish on Ubuntu. You will not use the secret file in this tutorial.

      • -s: This flag indicates where and how to store objects. The value malloc,256m is the default one for Vanish. It means to store various Varnish objects in memory using the malloc system call and a maximum size of 256 megabytes. Other possible values are default, which uses umem when malloc is not available, or file, which stores objects in a file on the disk.

      Save and close the customexec.conf file. Then execute this command to reload the systemd services file from disk:

      • sudo systemctl daemon-reload

      Then restart Varnish for changes to take effect.

      • sudo systemctl restart varnish

      You won’t see any output from these last two commands. To make sure that Varnish is now listening on port 8080, use the netstat command to display all listening TCP sockets on the server.

      • sudo netstat -ltnp | grep 8080

      You’ll see output that looks like this:

      Output

      tcp 0 0 0.0.0.0:8080 0.0.0.0:* LISTEN 18689/varnishd tcp6 0 0 :::8080 :::* LISTEN 18689/varnishd

      Now that Varnish is running and listening to port 8080, you need to edit the default configuration file located at /etc/varnish/default.vcl:

      • sudo nano /etc/varnish/default.vcl

      Navigate to the backend default block, and then change .port to 80, as shown here:

      default.vcl file

      # Default backend definition. Set this to point to your content server. backend default { .host = "127.0.0.1"; .port = "80"; }

      Save and close the default.vcl file, then restart Varnish with this command:

      • sudo systemctl restart varnish

      If everything is fine, there won’t be any output. Open http://your_server_ip:8080 in your browser, and you’ll see the Apache static site, opened using Varnish.

      You now have Apache and Varnish running together on the same Droplet, with Apache listening to port 80 and Varnish to port 8080. Next, you’ll compare the response times of both servers using the wrk tool.

      Step 3 — Testing Varnish Using wrk

      wrk is a modern HTTP benchmarking tool. It is written in C, and can be used to load test web servers with many requests per second. In this step, you’ll use wrk to run tests against Apache and Varnish and then compare the results.

      First you’ll need to install wrk by building it from source. Start by installing some build tools for C and git, which are required for building wrk from source:

      • sudo apt-get install build-essential libssl-dev git unzip -y

      Then clone the git repository for wrk into the wrk directory:

      • git clone https://github.com/wg/wrk.git wrk

      Change to that new directory:

      Build the wrk executable with the make command:

      Copy wrk to the /usr/local/bin directory so you can access it from anywhere in your directory structure:

      • sudo cp wrk /usr/local/bin

      Now that you have wrk installed, use it to test the responsiveness of Apache with this command:

      • wrk -t2 -c1000 -d30s --latency http://server_ip/

      This command uses the following arguments:

      • -t2: This means run two threads.
      • -c1000: Keep 1000 HTTP connections open.
      • -d30s: Run the test for 30 seconds.
      • --latency: Print latency statistics.

      Wait 30 seconds until the test is done, and you’ll see output similar to this:

      output

      Running 30s test @ http://68.183.115.151/ 2 threads and 1000 connections Thread Stats Avg Stdev Max +/- Stdev Latency 44.45ms 104.50ms 1.74s 91.20% Req/Sec 8.29k 1.07k 12.40k 71.00% Latency Distribution 50% 11.59ms 75% 22.73ms 90% 116.16ms 99% 494.90ms 494677 requests in 30.04s, 5.15GB read Socket errors: connect 0, read 8369, write 0, timeout 69 Requests/sec: 16465.85 Transfer/sec: 175.45MB

      In this test, the average latency is 44.45ms, there were 494,677 total requests, 8,369 read errors, and 69 timeout errors. The exact numbers will vary in your installation.

      Now run the same test again for the Varnish server using this command:

      • wrk -t2 -c1000 -d30s --latency http://server_ip:8080/

      Wait 30 seconds until the test is done, and you’ll see output similar to this:

      output

      Running 30s test @ http://68.183.115.151:8080/ 2 threads and 1000 connections Thread Stats Avg Stdev Max +/- Stdev Latency 14.41ms 13.70ms 602.49ms 90.05% Req/Sec 6.67k 401.10 8.74k 83.33% Latency Distribution 50% 13.03ms 75% 17.69ms 90% 24.72ms 99% 58.22ms 398346 requests in 30.06s, 4.18GB read Socket errors: connect 0, read 19, write 0, timeout 0 Requests/sec: 13253.60 Transfer/sec: 142.48MB

      The output you see will likely be somewhat different, but the latency will be lower for Varnish than for Apache. In this case, the average latency is 14.41ms, there were 398,346 total requests, and no errors.

      In these tests, with Apache the average response time was 44.45ms with 8,438 errors, while Varnish achieved an increase in speed to 14.41ms, and also had no errors. This is because Varnish cached the response in memory and served it for later requests, unlike Apache, which needs to read from disk almost every time the resource is requested.

      Conclusion

      In this tutorial, you configured Varnish as a reverse proxy caching server for a static web site. You saw how to use basic HTTP caching to improve performance, and you used wrk to run load tests for the Apache and Varnish servers to compare the results.

      You’ve seen that the Varnish cache server speeds up your static site by serving content from main memory and not requesting it from the back end Apache server every time a new request arrives. For more information about other uses of Varnish, see the official documentation.



      Source link

      How To Set Up Redis as a Cache for MySQL with PHP on Ubuntu 20.04


      The author selected Girls Who Code to receive a donation as part of the Write for DOnations program.

      Introduction

      Redis (Remote Dictionary Server) is a fast open-source, in-memory database that you can use as a key-value store for a highly scalable and performance-oriented system. Some of Redis’ use cases include: caching, high-speed transactions, real-time analytics, live notifications, machine learning, searching, and queue/job processing. Since Redis is an in-memory key-value store, its performance makes it suitable for caching data in your application.

      Caching is storing data temporarily in a high-speed storage layer (for example, in a computer RAM) to serve data faster when clients make the same future requests. This enhances the re-use of previously computed data instead of fetching it each time from the disk.

      When you’re working with PHP and MySQL, using Redis as a cache improves your application performance because Redis stores data in RAM, which is several times faster than a hard disk (HDD) or a solid-state drive (SSD). Caching also reduces database costs—that is, the number of round trips made to the back-end database—and avoids overloading the backend.

      Caching data is an integral design feature when you’re designing web applications with higher reads than writes. Such applications include blogs, online stores, and social media sites.

      In this tutorial, you’ll use Redis to cache MySQL data with PHP on Ubuntu 20.04.

      Prerequisites

      To complete this tutorial, you’ll need the following:

      Step 1 — Installing the Redis Library for PHP

      To begin you’ll install the php-redis extension, which will allow you to use PHP to communicate with Redis. Run the following commands to update your server and install the extension:

      • sudo apt update
      • sudo apt install php-redis

      Confirm the installation and restart the Apache web server to load the extension:

      • sudo systemctl restart apache2

      Now that you have installed your dependencies, you’ll set up your database.

      Step 2 — Setting Up a Test Database, Table, and Sample Data

      In this step, you’ll create a MySQL database to store data permanently to disk. You’ll also create some tables and a user account with full privileges to the database.

      First, log in to your MySQL server as a root user:

      Enter the root password of your MySQL server that you set up in the LAMP prerequisite. Then, press ENTER to continue.

      Next, create a test_store database with the following command:

      • CREATE database test_store;

      Make sure the action is successful by confirming the output:

      Output

      Query OK, 1 row affected (0.00 sec)

      Next, create a user for your database. We’ll call this user test_user in this tutorial. Replace PASSWORD with a strong password as well:

      • CREATE USER 'test_user'@'localhost' IDENTIFIED WITH mysql_native_password BY 'PASSWORD';

      Then grant test_user full privileges to the test_store database with:

      • GRANT ALL PRIVILEGES ON test_store.* TO 'test_user'@'localhost';

      Finally run the following command to reload the grant tables in MySQL:

      Ensure you get the following output after each successful command:

      Output

      Query OK, 0 rows affected (0.01 sec)

      End the MySQL root session:

      You’ll receive the word Bye and the system will take you back to the server’s command line interface.

      Log back in to the MySQL server with the credentials for the test_user that you just created:

      Enter the password for the test_user to proceed. Then, switch to the test_store database when you’re in the mysql> prompt:

      Ensure you receive the following output:

      Output

      Database Changed.

      Next, you’ll create a products table with three columns. You’ll use the product_id column to uniquely identify each product. To avoid assigning the IDs manually, you’ll use the AUTO_INCREMENT keyword. Then, you’ll use the BIGINT data type for the product_id column to support a large data set. The BIGINT data type can hold a minimum value of -2^63 and a maximum value of 2^63 - 1.

      The product_name field will hold the actual names of your items. In this case, a VARCHAR data type with a length of 50 characters will be enough. The last column in the products table is the price—you’ll use the DOUBLE data type to accommodate prices with decimals (for example, 16.33).

      To create the products table, run the following command:

      • CREATE table products
      • (
      • product_id BIGINT PRIMARY KEY AUTO_INCREMENT,
      • product_name VARCHAR(50),
      • price DOUBLE
      • ) Engine = InnoDB;

      You will receive the following output:

      Output

      Query OK, 0 rows affected (0.01 sec)

      Now you’ll populate the products table with some records for testing purposes.

      You don’t need to enter data to the product_id column manually since the AUTO_INCREMENT column will complete this. Run the following commands one by one:

      • INSERT INTO products(product_name, price) VALUES ('Virtual Private Servers', '5.00');
      • INSERT INTO products(product_name, price) VALUES ('Managed Databases', '15.00');
      • INSERT INTO products(product_name, price) VALUES ('Block Storage', '10.00');
      • INSERT INTO products(product_name, price) VALUES ('Managed Kubernetes', '60.00');
      • INSERT INTO products(product_name, price) VALUES ('Load Balancer', '10.00');

      After running each command, ensure you get this output:

      Output

      Query OK, 1 row affected (0.00 sec)

      Verify the data using the SELECT command:

      You will receive output similar to the following:

      Output

      +------------+-------------------------+-------+ | product_id | product_name | price | +------------+-------------------------+-------+ | 1 | Virtual Private Servers | 5 | | 2 | Managed Databases | 15 | | 3 | Block Storage | 10 | | 4 | Managed Kubernetes | 60 | | 5 | Load Balancer | 10 | +------------+-------------------------+-------+ 5 rows in set (0.00 sec)

      End the MySQL session for the test_user:

      Once you’ve set up the test_store database, products table, and test_user, you’ll code a PHP script to retrieve data from the MySQL database and cache it to Redis.

      Step 3 — Designing a PHP Script for Fetching and Caching MySQL Data

      In this step, you’ll create a PHP script for retrieving the sample data that you’ve created in the previous step.

      When you run the script for the first time, it will read the data from MySQL (that is, from disk) and then cache it to Redis. As a result subsequent reads of the products’ data will be from Redis (that is, from system RAM). System memory is multiple times faster than even the fastest solid-state drive, thus data will be retrieved faster from the Redis cache than reading from the system disk.

      Note: While you might not get any performance boost, since you are retrieving just a few records from the MySQL database, several benchmarks prove that retrieving cached data from Redis is several times faster than reading it from MySQL when dealing with several hundred thousand records.

      Create a products.php file in the root directory of your website:

      • sudo nano /var/www/html/products.php

      To start, enter the following information to connect and create an instance of Redis and store it as an object in a $redis variable.

      The address 127.0.0.1 connects to the localhost. You may change this value if you’re running Redis from a remote server. Remember to replace REDIS_PASSWORD with the specific password for Redis set in the /etc/redis/redis.conf configuration file.

      Also, enter the appropriate port number. By default, Redis runs on port 6379:

      /var/www/html/products.php

      <?php
      
      $redis = new Redis();
      $redis->connect('127.0.0.1', 6379);
      $redis->auth('REDIS_PASSWORD');
      

      Note: In this guide, the $redis->auth('REDIS_PASSWORD') command sends your password to Redis in plain text. In a production environment, you may consider securing end-to-end communication between Redis and the client server running PHP code with a more powerful access control layer, such as TLS (Transport Layer Security). Also, when configuring your Redis password in the /etc/redis/redis.conf file, make sure you set a long and strong value to prevent brute-force attacks.

      The next step is initializing a PHP variable you’ll use as a key in Redis.

      As mentioned earlier in this guide, Redis acts as a key-value database and therefore you must have a unique key for the data that you intend to store and retrieve from it.

      So, define a PRODUCTS key by adding the following information to the /var/www/html/products.php file. You are free to use any name in place of PRODUCTS key.

      Your PHP script will use this key to cache information to Redis once data gets retrieved from the MySQL database:

      /var/www/html/products.php

      ...
      $key = 'PRODUCTS';
      

      Next, include a conditional PHP if...else statement to check if the PRODUCTS key exists in Redis:

      /var/www/html/products.php

      ...
      if (!$redis->get($key)) {
          $source="MySQL Server";
          $database_name="test_store";
          $database_user="test_user";
          $database_password = 'PASSWORD';
          $mysql_host="localhost";
      
          $pdo = new PDO('mysql:host=" . $mysql_host . "; dbname=" . $database_name, $database_user, $database_password);
          $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
      
          $sql  = "SELECT * FROM products";
          $stmt = $pdo->prepare($sql);
          $stmt->execute();
      
          while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
             $products[] = $row;
          }
      
          $redis->set($key, serialize($products));
          $redis->expire($key, 10);
      
      } else {
           $source = "Redis Server';
           $products = unserialize($redis->get($key));
      
      }
      
      echo $source . ': <br>';
      print_r($products);
      

      If the key doesn’t exist in Redis, the script connects to the database that you created earlier, queries the products table, and stores the data in Redis using the $redis->set($key, serialize($products)) command.

      The $redis->expire($key, 10); command sets the expiration to 10 seconds. You may tweak this value depending on your cache policy.

      The $source variable helps you to identify the source of the data once it is echoed as an array at the end of the script using the echo $source and print_r($products) commands.

      Once you’ve put everything together, your /var/www/html/products.php file will be as follows:

      /var/www/html/products.php

      <?php
      
      $redis = new Redis();
      $redis->connect('127.0.0.1', 6379);
      $redis->auth('REDIS_PASSWORD');
      
      $key = 'PRODUCTS';
      
      if (!$redis->get($key)) {
          $source="MySQL Server";
          $database_name="test_store";
          $database_user="test_user";
          $database_password = 'PASSWORD';
          $mysql_host="localhost";
      
          $pdo = new PDO('mysql:host=" . $mysql_host . "; dbname=" . $database_name, $database_user, $database_password);
          $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
      
          $sql  = "SELECT * FROM products";
          $stmt = $pdo->prepare($sql);
          $stmt->execute();
      
          while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
             $products[] = $row;
          }
      
          $redis->set($key, serialize($products));
          $redis->expire($key, 10);
      
      } else {
           $source = "Redis Server';
           $products = unserialize($redis->get($key));
      
      }
      
      echo $source . ': <br>';
      print_r($products);
      
      

      Save and close the file.

      You’ve now set up a PHP script that will connect to MySQL and cache data to Redis. You’ll test your script in the next step.

      Step 4 — Testing the PHP Script

      To test if Redis is caching data from the MySQL database, you’ll enter the path of the PHP script in a browser window. Remember to replace your_server_IP with the public IP address of your server, like so: http://your_server_IP/products.php.

      When you run the script for the first time, you will receive the following output that displays data from the MySQL database because, at this point, the PHP script has not yet cached any data in Redis:

      MySQL Server
      Array ( [0] => Array ( [product_id] => 1 [product_name] => Virtual Private Servers [price] => 5 ) [1] => Array ( [product_id] => 2 [product_name] => Managed Databases [price] => 15 ) [2] => Array ( [product_id] => 3 [product_name] => Block Storage [price] => 10 ) [3] => Array ( [product_id] => 4 [product_name] => Managed Kubernetes [price] => 60 ) [4] => Array ( [product_id] => 5 [product_name] => Load Balancer [price] => 10 ) )
      

      Once you run the script again, you’ll get an output confirming that it’s reading data from Redis, which is acting as a cache for MySQL.

      Redis Server
      Array ( [0] => Array ( [product_id] => 1 [product_name] => Virtual Private Servers [price] => 5 ) [1] => Array ( [product_id] => 2 [product_name] => Managed Databases [price] => 15 ) [2] => Array ( [product_id] => 3 [product_name] => Block Storage [price] => 10 ) [3] => Array ( [product_id] => 4 [product_name] => Managed Kubernetes [price] => 60 ) [4] => Array ( [product_id] => 5 [product_name] => Load Balancer [price] => 10 ) )
      

      Remember that the key will expire after 10 seconds and data will again be retrieved from MySQL.

      Conclusion

      In this guide, you’ve used Redis to cache MySQL data with PHP on Ubuntu 20.04. You may use the coding in this guide to set up a caching mechanism for your MySQL data, which is especially useful for high-traffic web applications.

      You can check out our Redis topic page for more educational resources. Or, learn more about coding in PHP with further tutorials and content on the PHP topic page.



      Source link