Headless CMS pakai WordPress + Docker WSL2 Lambat?

Updated: September 29, 2025 at 04:02 PM

3 min read

Solusi 1: Implementasi Caching lewat nginx

Lewat nginx, kita bisa melakukan caching sebelum konten ini diantarkan ke user. Dengan pendekatan ini, jika sudah ada yang membuka suatu item, item ini akan tinggal di cache di server maka selanjutnya (selama cache masih valid) data akan di serve langsung dari cache, bukan lewat PHP process lagi. Ini bisa membawa speed yang cukup signifikan.

Kondisi “coldstart”. Nginx belum punya cache dari item ini, jadi nginx hanya proxy untuk serve konten dari Apache.

Sebelum saya melakukan cache ini, untuk mengakses homepage saja butuh waktu 2 detik (2100ms) sebelum server merespons request kita. Ini akan membuat website terasa “lelet“. Setelah nginx cache, response time server turun jadi hanya 75ms! Ini sangat signifikan.

Reload page dan bisa kita lihat ini adalah request API setelah di cache oleh nginx. Response time hanya butuh 75ms!

Pendekatan ini ada kekurangannya, yaitu kalau item ini belum ada yang buka sama sekali, nginx pasti tak punya cache untuk di serve dan harus menunggu Apache + mod_php  (WordPress) untuk merender konten terlebih dahulu. Ini lah yang menyebabkan situasi saat “coldstart” menjadi sangat lama

Konfigurasi compose.yml

services:
  wordpress:
    image: wordpress
    restart: always
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: (username database)
      WORDPRESS_DB_PASSWORD: (password database)
      WORDPRESS_DB_NAME: dws-notes-db
      WORDPRESS_HOME: http://wp.server.drl
      WORDPRESS_SITEURL: http://wp.server.drl
      WORDPRESS_DEBUG: false
      PHP_OPCACHE_ENABLE: 1
      PHP_OPCACHE_MEMORY_CONSUMPTION: 128
      PHP_OPCACHE_MAX_ACCELERATED_FILES: 10000
      PHP_OPCACHE_REVALIDATE_FREQ: 0
    volumes:
      - ./wordpress:/var/www/html

  nginx:
    image: nginx:alpine
    restart: always
    ports:
      - 8055:80
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./wordpress:/var/www/html:ro
      - nginx-cache:/var/cache/nginx
    depends_on:
      - wordpress

  db:
    image: mysql:8.0
    restart: always
    environment:
      MYSQL_DATABASE: dws-notes-db
      MYSQL_USER: (username database)
      MYSQL_PASSWORD: (password database)
      MYSQL_RANDOM_ROOT_PASSWORD: '1'
    volumes:
      - ./db:/var/lib/mysql
    deploy:
      resources:
        limits:
          memory: 350M
        reservations:
          memory: 64M
    command: [
      '--innodb-buffer-pool-size=128M',
      '--innodb-log-file-size=16M',
      '--performance-schema=0'
    ]

volumes:
  nginx-cache:
    driver: local

Konfigurasi nginx.conf:

events {
    worker_connections 1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    sendfile        on;
    keepalive_timeout  65;

    gzip on;
    gzip_vary on;
    gzip_min_length 1024;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_types
        text/plain
        text/css
        text/xml
        text/javascript
        application/json
        application/javascript
        application/xml+rss
        application/atom+xml
        image/svg+xml;

    proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=WORDPRESS_CACHE:200m inactive=120m max_size=1024m;
    proxy_cache_key "$scheme://$host$request_uri";

    # don't cache for logged-in users or when specific cookies are present
    map $http_cookie $no_cache {
        default 0;
        ~SESS 1;
        ~wordpress_logged_in 1;
    }

    upstream wordpress {
        server wordpress:80;
        keepalive 16;
    }

    server {
        listen 80;
        server_name localhost;
        root /var/www/html;
        index index.php index.html index.htm;

        location / {
            try_files $uri $uri/ @wp;
        }

        location @wp {
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto http;

            proxy_cache WORDPRESS_CACHE;
            proxy_cache_bypass $no_cache;
            proxy_no_cache $no_cache;
            proxy_cache_valid 200 10m;
            proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
            proxy_cache_lock on;
            proxy_http_version 1.1;
            proxy_set_header Connection "";
            proxy_buffering on;
            proxy_buffers 8 16k;
            proxy_busy_buffers_size 32k;

            add_header X-Cache-Status $upstream_cache_status;

            proxy_pass http://wordpress;
        }

        location ~ \.php$ {
            proxy_pass http://wordpress;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto http;
            proxy_http_version 1.1;
            proxy_set_header Connection "";
            proxy_buffering on;
            proxy_buffers 8 16k;
            proxy_busy_buffers_size 32k;
        }

        location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
            expires 1y;
            add_header Cache-Control "public, immutable";
            access_log off;
        }

        location ~ /\.ht {
            deny all;
        }
    }
}

Solusi 2: Ganti Apache + mod_php sepenuhnya dan pakai stack nginx + PHP-FPM

Solusi ini nyaris bisa dibilang “drop-in replacement” karena kita tidak menggunakan Apache untuk serving dan processing file PHP dan kita langsung menggunakan nginx.

Saat cold start. 1006ms not bad lah untuk server 3 core dan running di Docker WSL2 + HDD 😁
Setelah menggunakan stack nginx + PHP-FPM + Caching. 69ms response time! (Ini production, jadi via internet bukan via localhost)

Konfigurasi lengkap bisa cek lewat Repository GitHub disini

Sekian notes kali ini, selamat bereksperimen!