NGINX FastCGI Cache untuk WordPress Docker

Setup

Aku host WordPress aku sendiri guna Docker — NGINX + WordPress + MySQL dalam container berasingan. Baru-baru ni aku enable NGINX FastCGI Cache dan hasilnya memang nampak beza.

Apa FastCGI Cache Buat

Dia simpan versi statik HTML kat level NGINX. Bila visitor hit page yang sama, NGINX serve terus dari cache tanpa perlu go through WordPress → PHP → MySQL. Jimat resource, laju gila.

Docker Compose (simplified)

services:
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./nginx/default.conf:/etc/nginx/conf.d/default.conf
      - ./wordpress:/var/www/html
      - nginx-cache:/var/run/nginx-cache
    depends_on:
      - wordpress

  wordpress:
    image: wordpress:php8.2-fpm
    volumes:
      - ./wordpress:/var/www/html
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_NAME: wp
      WORDPRESS_DB_USER: wp
      WORDPRESS_DB_PASSWORD: secret

  db:
    image: mysql:8.0
    volumes:
      - db-data:/var/lib/mysql
    environment:
      MYSQL_DATABASE: wp
      MYSQL_USER: wp
      MYSQL_PASSWORD: secret
      MYSQL_ROOT_PASSWORD: rootsecret

volumes:
  db-data:
  nginx-cache:

Yang penting: NGINX dan WordPress share volume /var/www/html, dan ada dedicated volume untuk cache.

NGINX Config

nginx.conf (main context)

http {
    # FastCGI cache path
    fastcgi_cache_path /var/run/nginx-cache levels=1:2
        keys_zone=WORDPRESS:100m
        inactive=60m
        max_size=512m;

    fastcgi_cache_key "$scheme$request_method$host$request_uri";

    # Cache status header — so kita boleh verify
    add_header X-FastCGI-Cache $upstream_cache_status;

    include /etc/nginx/conf.d/*.conf;
}

default.conf (server block)

server {
    listen 80;
    server_name azmi.my;
    root /var/www/html;
    index index.php;

    # Skip cache for logged-in users and POST requests
    set $skip_cache 0;

    if ($request_method = POST) {
        set $skip_cache 1;
    }

    if ($http_cookie ~* "wordpress_logged_in") {
        set $skip_cache 1;
    }

    if ($request_uri ~* "/wp-admin/|/wp-json/|/xmlrpc.php") {
        set $skip_cache 1;
    }

    location / {
        try_files $uri $uri/ /index.php?$args;
    }

    location ~ \.php$ {
        fastcgi_pass wordpress:9000;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;

        # Cache settings
        fastcgi_cache WORDPRESS;
        fastcgi_cache_valid 200 60m;
        fastcgi_cache_valid 404 1m;
        fastcgi_cache_bypass $skip_cache;
        fastcgi_no_cache $skip_cache;
    }

    # Browser cache for static files
    location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff|woff2|ttf|eot)$ {
        expires 30d;
        add_header Cache-Control "public, immutable";
    }
}

Apa Yang Berlaku

  • First visit — NGINX forward ke PHP-FPM, WordPress generate page, NGINX simpan copy dalam cache
  • Second visit (same page) — NGINX serve terus dari cache, PHP tak disentuh langsung
  • /wp-admin, logged-in users, POST requests — bypass cache (tak nak serve stale admin pages)
  • Static files (.css, .js, images) — browser cache 30 hari

Verify Cache Working

curl -I https://azmi.my/

# Look for this header:
X-FastCGI-Cache: HIT

Possible values:

  • HIT — served from cache ✅
  • MISS — not in cache, generated fresh (first visit)
  • BYPASS — skipped cache (logged in / POST / wp-admin)

Result

  • Response time bawah 100ms untuk cached pages (sebelum ni 800ms+)
  • PageSpeed score naik — especially Time to First Byte (TTFB)
  • Server load turun drastik — PHP-FPM barely touched untuk normal traffic
  • Boleh handle lebih banyak concurrent visitors tanpa upgrade server

Cache Purge

Bila update post, cache kena clear. Dua cara:

Manual:

# Clear semua cache
rm -rf /var/run/nginx-cache/*

# Atau reload nginx
docker exec nginx nginx -s reload

Auto (plugin): Install “Nginx Helper” plugin dalam WordPress. Set purge on post update. Dia tulis ke file yang NGINX monitor.

Tips Tambahan

  • Redis object cache — tambah Redis container + WP Redis plugin untuk cache database queries. Lagi satu layer speed.
  • Cache exceptions — kalau ada WooCommerce atau dynamic pages, add more skip rules
  • Monitor cache hit ratio — check access log, grep for HIT/MISS/BYPASS. Kalau banyak MISS, something wrong.
  • Disk spacemax_size=512m cukup untuk most blogs. Monitor /var/run/nginx-cache size.

Kombinasi NGINX FastCGI Cache + Docker + WordPress — prestasi tinggi, takde kos tambahan, takde plugin berat. Kalau korang self-host WordPress, try la.

Leave a Comment