---
title: 'Высокопроизводительный WordPress в Docker: Caddy TLS - Nginx кэш - PHP-FPM - real IP | Zomro'
description: ''
image: 'https://cdn.zomro.com/uploads/blog/a48%40server-panel.net/2025/10/31/05_q50.jpg'
---

![opened image]()

  * [Zomro](https://zomro.com/rus/)
  * [FAQ](https://zomro.com/rus/blog/faq/)
  * Высокопроизводительный WordPress в Docker: Caddy TLS - Nginx кэш - PHP-FPM - real IP 



# Высокопроизводительный WordPress в Docker: Caddy TLS - Nginx кэш - PHP-FPM - real IP

12-10-2025

В этой статье мы рассмотрим как поднять стек **Caddy (HTTPS, HTTP/2/3)** → **Nginx (fastcgi_cache + security)** → **WordPress-PHP-FPM** → **MariaDB** → **Redis**. Реальный IP клиента берём через `X-Forwarded-For` от Caddy. Добавим системные тюнинги (io_uring, THP, QUIC-буферы) и чек-лист проверки.

> Документация: Docker / Compose, Nginx, Caddy, WordPress, MariaDB, Redis.

## Подготовка каталога и файлов

> Заведомо установите `Docker` и `Docker Compose`

Создайте необходимые директории:
    
    
    mkdir -p /opt/wordpress/{caddy,nginx,php,conf.d} && cd /opt/wordpress

Структура:
    
    
    project-root/
    │
    ├─ docker-compose.yml
    │
    ├─ php/
    │   └─ uploads.ini
    │
    ├─ nginx/
    │   ├─ nginx.conf
    │   └─ conf.d/
    │       └─ wordpress.conf
    │
    └─ caddy/
        └─ Caddyfile
    

## 

📌 **Пояснения:**

  * **`php/uploads.ini`** — задаёт лимиты (загрузка, память, время выполнения).

  * **`nginx/nginx.conf`** — основная конфигурация Nginx (включает fastcgi_cache, gzip и логи).

  * **`nginx/conf.d/wordpress.conf`** — отдельный виртуальный хост WordPress с правилами кэша и безопасностью.

  * **`caddy/Caddyfile`** — прокси с HTTPS и автообновлением сертификатов Let’s Encrypt.




## 

## Конфигурации

### `docker-compose.yml` (ключевые моменты)

  * **Nginx** монтируем **папку** `conf.d` (чтобы скрыть дефолтный `default.conf` из образа).

  * **Caddy** : принимает 80/443, проксирует на `nginx:80` и прокидывает реальный IP клиента.

  * **WordPress** : `php-fpm` образ, `WORDPRESS_CONFIG_EXTRA` с базовыми дефайнами.

  * **Redis** : для object-cache.

  * **MariaDB** : LTS, utf8mb4.




_(ниже — только важные элементы; свои пароли/домены подставьте)_
    
    
    services:
      db:
        image: mariadb:10.11
        command:
          - --innodb-buffer-pool-size=256M
          - --character-set-server=utf8mb4
          - --collation-server=utf8mb4_unicode_ci
          - --innodb-file-per-table=1
          - --server-id=1
          - --log-bin=mysql-bin
          - --binlog-expire-logs-seconds=604800 
        environment:
          MYSQL_ROOT_PASSWORD: '<root_password>'
          MYSQL_DATABASE: stm_wordpress
          MYSQL_USER: stm_wordpress_user
          MYSQL_PASSWORD: '<db_password>'
          TZ: Europe/Amsterdam
        volumes:
          - db_data:/var/lib/mysql
        restart: always
    
      redis:
        image: redis:7-alpine
        command: ["redis-server", "--appendonly", "yes"]
        restart: always
    
      wordpress:
        image: wordpress:6.8.3-php8.3-fpm
        depends_on:
          - db
          - redis
        environment:
          WORDPRESS_DB_HOST: "db:3306"
          WORDPRESS_DB_USER: stm_wordpress_user
          WORDPRESS_DB_PASSWORD: '<wp_db_password>'
          WORDPRESS_DB_NAME: stm_wordpress
          TZ: Europe/Amsterdam
          WORDPRESS_CONFIG_EXTRA: |
            if (!defined('WP_CACHE')) define('WP_CACHE', true);
            if (!defined('DISALLOW_FILE_EDIT')) define('DISALLOW_FILE_EDIT', true);
            if (!defined('AUTOMATIC_UPDATER_DISABLED')) define('AUTOMATIC_UPDATER_DISABLED', true);
            if (!defined('FS_METHOD')) define('FS_METHOD', 'direct');
            if (!defined('WP_MEMORY_LIMIT')) define('WP_MEMORY_LIMIT', '256M');
            if (!defined('WP_MAX_MEMORY_LIMIT')) define('WP_MAX_MEMORY_LIMIT', '256M');
            if (!defined('WP_DEBUG')) define('WP_DEBUG', false);
            if (!defined('WP_POST_REVISIONS')) define('WP_POST_REVISIONS', 5);
            if (!defined('EMPTY_TRASH_DAYS')) define('EMPTY_TRASH_DAYS', 7);
            define('WP_REDIS_HOST', 'redis');
            define('WP_REDIS_PORT', 6379);
            /* HTTPS за прокси (Caddy) */
            if (isset($$_SERVER['HTTP_X_FORWARDED_PROTO']) && $$_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
              $$_SERVER['HTTPS'] = 'on';
            }
        volumes:
          - wp_data:/var/www/html
          - ./php/uploads.ini:/usr/local/etc/php/conf.d/uploads.ini:ro
        restart: always
    
      nginx:
        image: nginx:1.27-alpine
        depends_on:
          - wordpress
        # Порты наружу НЕ открываем — к нему ходит только Caddy
        volumes:
          - wp_data:/var/www/html:ro
          - nginx_cache:/var/cache/nginx
          - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
          - ./nginx/conf.d/wordpress.conf:/etc/nginx/conf.d/wordpress.conf:ro
        restart: always
        logging:
          driver: json-file
          options:
            max-size: "10m"
            max-file: "3"
    
      caddy:
        image: caddy:2.8-alpine
        depends_on:
          - nginx
        ports:
          - "80:80"
          - "443:443"
        environment:
          DOMAIN: your.site
          ACME_EMAIL: admin@your.site
        volumes:
          - ./caddy/Caddyfile:/etc/caddy/Caddyfile:ro
          - caddy_data:/data
          - caddy_config:/config
        restart: always
    
    volumes:
      db_data:
      wp_data:
      nginx_cache:
      caddy_data:
      caddy_config:

### 

Где:

  * **DOMAIN** : - ваш сайт (домен)
  * **ACME_EMAIL** : admin@ваш сайт (домен)



### `php/uploads.ini`
    
    
    file_uploads=On
    memory_limit=256M
    upload_max_filesize=64M
    post_max_size=64M
    max_execution_time=120
    max_input_vars=5000
    

### 

### `nginx/nginx.conf`
    
    
    user  nginx;
    worker_processes  auto;
    
    events { worker_connections 1024; }
    
    http {
      include       /etc/nginx/mime.types;
      default_type  application/octet-stream;
    
      sendfile        on;
      tcp_nopush      on;
      tcp_nodelay     on;
      keepalive_timeout  65;
    
      gzip on;
      gzip_comp_level 5;
      gzip_min_length 1024;
      gzip_types text/plain text/css application/json application/javascript application/xml+rss application/xml application/x-javascript image/svg+xml;
      gzip_vary on;
    
      access_log /var/log/nginx/access.log;
      error_log  /var/log/nginx/error.log warn;
    
      fastcgi_cache_path /var/cache/nginx/wordpress levels=1:2 keys_zone=WORDPRESS:100m inactive=60m max_size=1g;
      fastcgi_cache_key "$scheme$request_method$host$request_uri";
    
      set_real_ip_from 172.18.0.0/16; #your docker IP
    
      real_ip_header   X-Forwarded-For;
      real_ip_recursive on;
    
      log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" "$http_user_agent" '
                      '"$http_x_forwarded_for" realip=$realip_remote_addr';
      access_log /var/log/nginx/access.log main;
    
      include /etc/nginx/fastcgi_params;
      include /etc/nginx/conf.d/*.conf;
    }
    

### 

### `nginx/conf.d/wordpress.conf` (fastcgi_cache + security)
    
    
    server {
      listen 80 default_server;
      server_name _;
      root /var/www/html;
    
      add_header X-Frame-Options "SAMEORIGIN" always;
      add_header X-Content-Type-Options "nosniff" always;
      add_header Referrer-Policy "strict-origin-when-cross-origin" always;
      add_header Permissions-Policy "geolocation=(), microphone=(), camera=(), payment=(), usb=()" always;
      add_header X-XSS-Protection "1; mode=block" always;
    
      index index.php index.html;
    
      location ~* /(\.git|\.svn|\.hg|composer\.json|composer\.lock|package\.json|gulpfile\.js|Gruntfile\.js|webpack\.config\.js) { deny all; }
      location ~* /(wp-content|wp-includes|uploads|files)/.*\.php$ { deny all; }
      location = /xmlrpc.php { deny all; }
    
      # micro-cache
      set $skip_cache 0;
      if ($request_method = POST) { set $skip_cache 1; }
      if ($query_string != "")   { set $skip_cache 1; }
      if ($request_uri ~* "/wp-admin/|/wp-login.php|preview=true") { set $skip_cache 1; }
      if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wordpress_logged_in") { set $skip_cache 1; }
    
      location ~* \.(?:css|js|jpg|jpeg|gif|png|svg|webp|ico|ttf|otf|woff|woff2)$ {
        access_log off; log_not_found off;
        expires 30d;
        add_header Cache-Control "public, max-age=2592000, immutable";
        try_files $uri =404;
      }
    
      location / {
        try_files $uri $uri/ /index.php?$args;
      }
    
      location ~ \.php$ {
        try_files $uri =404;
    
        include /etc/nginx/fastcgi_params;
        fastcgi_pass wordpress:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    
        fastcgi_cache_bypass $skip_cache;
        fastcgi_no_cache     $skip_cache;
        fastcgi_cache        WORDPRESS;
        fastcgi_cache_valid  200 301 302 1m;
        fastcgi_cache_valid  404 1m;
        add_header X-FastCGI-Cache $upstream_cache_status;
      }
    
      if ($request_method !~ ^(GET|HEAD|POST)$ ) { return 405; }
    }
    

### 

### `caddy/Caddyfile` (TLS + реальный IP → Nginx)
    
    
    {
      email {$ACME_EMAIL}
    }
    
    {$DOMAIN} {
      encode zstd gzip
    
      header {
        Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
        Content-Security-Policy "default-src 'self' 'unsafe-inline' 'unsafe-eval' https: data: blob:"
        Referrer-Policy "strict-origin-when-cross-origin"
        X-Frame-Options "SAMEORIGIN"
        X-Content-Type-Options "nosniff"
        X-XSS-Protection "1; mode=block"
        Permissions-Policy "geolocation=(), microphone=(), camera=(), payment=(), usb=()"
      }
    
      reverse_proxy nginx:80 {
        header_up X-Forwarded-For {remote}
        header_up X-Real-IP      {remote}
        header_up Host           {host}
      }
    }
    
    :80 {
      encode zstd gzip
      redir https://{$DOMAIN}{uri} permanent
    }
    

## 

## Запуск
    
    
    docker compose up -d
    

Откройте `https://<ваш_домен>` → завершите установку WordPress.

![](https://cdn.zomro.com/uploads/blog/a48%40server-panel.net/2025/10/31/05_q50.jpg)

В админке установите плагин, **Redis Object Cache,** включите **Redis Object Cache** → _Enable_.

![](https://cdn.zomro.com/uploads/blog/a48%40server-panel.net/2025/10/31/07_q50.jpg)

![](https://cdn.zomro.com/uploads/blog/a48%40server-panel.net/2025/10/31/08_q50.jpg)

## Проверка real IP
    
    
    docker compose exec nginx nginx -t
    docker compose logs --tail=100 nginx
    

![](https://cdn.zomro.com/uploads/blog/a48%40server-panel.net/2025/10/31/06_q50.jpg)

В `access.log` ожидаем:

  * `"$http_x_forwarded_for"` — не пустой;

  * `realip=<публичный_IP_клиента>` (не `172.18.*`).




Если видите 172.18.*:

  * Проверьте, что **Caddy** отправляет `X-Forwarded-For {remote}`;

  * В `nginx.conf` нет дублей `real_ip_header`/`real_ip_recursive`;

  * В `docker-compose.yml` монтируется **директория** `nginx/conf.d` (а не `default.conf` из образа).




## Производительность и стабильность хоста (опционально)

### Redis: overcommit + THP off (постоянно)
    
    
    echo 'vm.overcommit_memory = 1' | sudo tee /etc/sysctl.d/90-redis.conf
    sudo sysctl --system

### Transparent Huge Pages: постоянное отключение `systemd-юнитом:`
    
    
    sudo tee /etc/systemd/system/disable-thp.service >/dev/null <<'EOF'
    [Unit]
    Description=Disable Transparent Huge Pages
    After=multi-user.target
    
    [Service]
    Type=oneshot
    ExecStart=/bin/sh -c 'echo never > /sys/kernel/mm/transparent_hugepage/enabled'
    ExecStart=/bin/sh -c 'echo never > /sys/kernel/mm/transparent_hugepage/defrag'
    RemainAfterExit=yes
    
    [Install]
    WantedBy=multi-user.target
    EOF

Перезапуск/применение:
    
    
    sudo systemctl daemon-reload
    sudo systemctl enable --now disable-thp.service

Проверка:
    
    
    cat /sys/kernel/mm/transparent_hugepage/enabled   # [...] [never]
    cat /sys/kernel/mm/transparent_hugepage/defrag    # [...] [never]
    

### 

### MariaDB: io_uring
    
    
    # лучший вариант: включить
    sudo sysctl kernel.io_uring_disabled=0
    echo "kernel.io_uring_disabled = 0" | sudo tee /etc/sysctl.d/99-io-uring.conf
    sudo sysctl --system
    
    # проверка
    sysctl kernel.io_uring_disabled   # 0
    

### 

### Caddy: HTTP/3 (QUIC) — увеличить UDP-буферы
    
    
    echo -e "net.core.rmem_max=7500000\nnet.core.wmem_max=7500000\nnet.core.rmem_default=262144\nnet.core.wmem_default=262144" | sudo tee /etc/sysctl.d/90-caddy-quic.conf
    sudo sysctl --system
    docker compose restart caddy
    

## 

## 

## Плагины WordPress (минимум)

  * **Redis Object Cache** — обязательно.

  * **Limit Login Attempts Reloaded** — защита `/wp-login.php`.

  * **WP Crontrol** , **Health Check & Troubleshooting** — диагностика.




> Не ставьте сторонние page-cache (W3TC/LiteSpeed/etc.) — кеш уже делает Nginx (fastcgi_cache).

## Если позже включите Cloudflare

  * Переключите Nginx на `real_ip_header CF-Connecting-IP` и прокиньте его из Caddy, **или** добавьте все CIDR Cloudflare в `set_real_ip_from` и оставьте `X-Forwarded-For`.




## Резюме

  * Реальный IP **без Cloudflare** : Caddy выставляет `X-Forwarded-For {remote}`, Nginx доверяет docker-мосту и читает IP из XFF.

  * HTTPS/HTTP2/3 и автосертификаты Let’s Encrypt — у Caddy.

  * Производительность: Nginx fastcgi_cache + Redis object-cache, тюнинги хоста для стабильности.

  * Логи показывают корректный IP клиента, rate-limit/WAF/аналитика работают правильно.




Похожие статьи

[Как заказать выделенный сервер в новом биллинге](https://zomro.com/rus/blog/faq/230-kak-zakazat-vydelennyj-server-v-novom-billinge) [cPanel: создание почтового ящика и работа с ним](https://zomro.com/rus/blog/faq/222-cpanel-sozdanie-pochtovogo-jaschika-i-rabota-s-nim) [Как установить и настроить PHP на Windows Server](https://zomro.com/rus/blog/faq/363-how-to-install-and-configure-php-on-windows-server) [Установка индивидуального пароля в Filegator](https://zomro.com/rus/blog/faq/414-ustanovka-individualnogo-parolya-v-filegator) [Как установить FileBrowser в Docker](https://zomro.com/rus/blog/faq/293-kak-ustanovit-file-browser-v-docker)

[ Виртуальный хостинг от €3.98/мес ](/rus/virtual)
