Run WordPress on Nginx Unit with full page cache

Run WordPress on Nginx Unit with full page cache

Nginx Unit is an open-source application server that is designed to provide a flexible and lightweight platform for hosting applications written in various programming languages. It was developed by Nginx, Inc. and was released in 2017.

Nginx Unit allows you to run multiple applications on a single server instance, which can help reduce infrastructure costs and improve scalability. It supports various programming languages such as Python, PHP, Ruby, Node.js, and Go, among others.

Some of the key features of Nginx Unit include:

  1. Multi-language support: Nginx Unit supports multiple programming languages, allowing you to run a wide range of applications on a single server instance.

  2. Dynamic configuration: Nginx Unit allows you to modify its configuration dynamically without restarting the server, which can help improve availability and reduce downtime.

  3. API-driven: Nginx Unit is API-driven, which means you can manage it programmatically, making it easier to automate application deployment and management.

  4. High performance: Nginx Unit is designed to be lightweight and high-performance, which makes it ideal for running applications in production environments.

For run our site we will use official Nginx Unit Docker image and install to it some dependencies such as WP CLI, ImageMagick, mysql, opcache and redis php extensions:

FROM nginx/unit:1.26.1-php8.0

RUN apt-get update && apt-get install -y --no-install-recommends \
    curl \
    cron \
    autoconf \
    libz-dev \
    libssl-dev \
    libmagickwand-dev \
    libzip-dev \
    zip \
    libicu-dev \
    && docker-php-ext-install pdo_mysql mysqli opcache soap exif zip intl \
    && pecl install imagick redis && docker-php-ext-enable imagick redis \
    && rm -rf /var/lib/apt/lists/*

RUN cd ~ && curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar \
    && chmod +x wp-cli.phar \
    && mv wp-cli.phar /usr/local/bin/wp

RUN usermod -u 1000 www-data

COPY ./*.json /docker-entrypoint.d/

WORKDIR /var/www

Nginx Unit config example for WordPress (config.json) :

{
  "listeners": {
    "*:9000": {
      "pass": "routes"
    }
  },
  "routes": [
    {
      "match": {
        "uri": [
          "*.php",
          "*.php/*",
          "/wp-admin/"
        ]
      },
      "action": {
        "pass": "applications/wordpress/direct"
      }
    },
    {
      "action": {
        "share": "/var/www/site/$uri",
        "fallback": {
          "pass": "applications/wordpress/index"
        }
      }
    }
  ],
  "applications": {
    "wordpress": {
      "type": "php",
      "targets": {
        "direct": {
          "root": "/var/www/site/"
        },
        "index": {
          "root": "/var/www/site/",
          "script": "index.php"
        }
      },
      "processes": {
        "max": 20,
        "spare": 10
      }
    }
  }
}

Also we install WP Fastest Cache plugin and enable full page caching in plugin options.

For serve cached pages directly by Nginx we will use next Nginx config:

upstream php-upstream { 
    server unit:9000; 
}

server {
    listen 80 default_server;

    server_name mysite.com;
    root /var/www/site;

    location = /favicon.ico { access_log off; log_not_found off; }


    location / {
        error_page 418 = @cachemiss;
        recursive_error_pages on;

        set $path "/app/cache/all${uri}";

        # bypass POST requests
        if ($request_method = POST) { return 418; }

        # uncommenting the following degrades the performance on certain sites. YMMV
        if ($query_string != "") { return 418; }

        # bypass cache for common query strings
        if ($arg_s != "") { return 418; } # search query
        if ($arg_p != "") { return 418; } # request a post / page by ID
        if ($arg_preview = "true") { return 418; } # preview post / page

        # if WP related cookies are found, skip cache
        if ($http_cookie ~* "wordpress_logged_in_") { return 418; }
        if ($http_cookie ~* "comment_author_") { return 418; }
        if ($http_cookie ~* "wp_postpass_") { return 418; }

        try_files "${path}index.html" "${path}/index.html" $uri @unit;

        add_header "X-Cache" "HIT";
    }

    location @cachemiss {
        # on cache miss, send the request to PHP
        try_files $uri @unit;
    }

    location ~* .php$ {
        try_files        $uri =404;
        proxy_pass       http://php-upstream;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;
    }

    location @unit {
        proxy_pass http://php-upstream;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;
    }

    location ~* \.(ico|css|js|gif|png|jpg|webp|svg|woff|ttf|otf|svg|woff2|eot)$ {
        expires max;
        add_header Cache-Control "public";
    }

    location ~ /\. {
        deny all;
    }
}

Read more