Другое

Почему PHP-FPM возвращает 404 в Docker-конфигурации Laravel

Решение проблемы 404 ошибок при настройке PHP-FPM с Laravel в Docker. Пошаговое исправление конфигурации nginx и docker-compose для корректной работы API.

Почему PHP-FPM возвращает 404 для всех запросов в моей Docker‑конфигурации?

У меня есть три контейнера:

  • nginx
  • backend: php‑fpm + Laravel
  • frontend: Vue.js + nginx

Я настроил так, чтобы все запросы на /api/* перенаправлялись на backend в Laravel, а остальные запросы — на frontend. Frontend работает нормально, страницы открываются, но backend возвращает 404 для всех запросов.

Где я допустил ошибку в конфигурации?

yaml
# docker-compose.yml
services:
  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile
      args:
        HTTP_PROXY: http://172.17.0.1:8118
        HTTPS_PROXY: http://172.17.0.1:8118
        NO_PROXY: localhost,127.0.0.1,::1,host.docker.internal,172.17.0.1,db,redis,backend,mailhog,frontend,web,adminer
        http_proxy: http://172.17.0.1:8118
        https_proxy: http://172.17.0.1:8118
        no_proxy: localhost,127.0.0.1,::1,host.docker.internal,172.17.0.1,db,redis,backend,mailhog,frontend,web,adminer
      extra_hosts:
        - "host.docker.internal:host-gateway"
    container_name: jdasia-backend
    env_file:
      - ./docker/.env.docker
    volumes:
      - ./backend:/var/www/html
    depends_on:
      - db
      - redis
    environment:
      XDEBUG_MODE: ${XDEBUG_MODE:-develop,debug}
      XDEBUG_CLIENT_HOST: ${XDEBUG_CLIENT_HOST:-host.docker.internal}
      XDEBUG_CLIENT_PORT: ${XDEBUG_CLIENT_PORT:-9003}
      http_proxy: http://172.17.0.1:8118
      https_proxy: http://172.17.0.1:8118
      no_proxy: localhost,127.0.0.1,::1,host.docker.internal,172.17.0.1,db,redis,backend,mailhog,frontend,web,adminer
      HTTP_PROXY: http://172.17.0.1:8118
      HTTPS_PROXY: http://172.17.0.1:8118
      NO_PROXY: localhost,127.0.0.1,::1,host.docker.internal,172.17.0.1,db,redis,backend,mailhog,frontend,web,adminer
    extra_hosts:
      - "host.docker.internal:host-gateway"

  nginx:
    image: nginx:stable-alpine
    container_name: jdasia-nginx
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./infra/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
      - ./infra/nginx/certs:/etc/nginx/certs:ro
    depends_on:
      - backend
      - frontend

  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile
      args:
        HTTP_PROXY: http://172.17.0.1:8118
        HTTPS_PROXY: http://172.17.0.1:8118
        NO_PROXY: localhost,127.0.0.1,::1,host.docker.internal,172.17.0.1,db,redis,backend,mailhog,frontend,web,adminer
        http_proxy: http://172.17.0.1:8118
        https_proxy: http://172.17.0.1:8118
        no_proxy: localhost,127.0.0.1,::1,host.docker.internal,172.17.0.1,db,redis,backend,mailhog,frontend,web,adminer
      extra_hosts:
        - "host.docker.internal:host-gateway"
    container_name: jdasia-frontend
    volumes:
      - ./frontend:/app
      - /app/node_modules
      - ./infra/nginx/certs:/etc/nginx/certs:ro
    environment:
      - NODE_ENV=development
      - HTTP_PROXY=http://172.17.0.1:8118
      - HTTPS_PROXY=http://172.17.0.1:8118
      - NO_PROXY=localhost,127.0.0.1,::1,host.docker.internal,172.17.0.1,db,redis,backend,mailhog,frontend,web,adminer
      - http_proxy=http://172.17.0.1:8118
      - https_proxy=http://172.17.0.1:8118
      - no_proxy=localhost,127.0.0.1,::1,host.docker.internal,172.17.0.1,db,redis,backend,mailhog,frontend,web,adminer
    depends_on:
      - backend

volumes:
  db_data:
dockerfile
# ./backend/Dockerfile
FROM php:8.2-fpm-trixie

# Proxy support
ARG HTTP_PROXY
ARG HTTPS_PROXY
ARG NO_PROXY
# Only set proxy if explicitly provided
ENV http_proxy=${HTTP_PROXY:-} \
    https_proxy=${HTTPS_PROXY:-} \
    no_proxy=${NO_PROXY:-} \
    ALL_PROXY=${HTTP_PROXY:-}

# Install system deps (Debian/apt)
RUN apt-get update \
  && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
       git curl unzip zip \
       tcpdump mc htop \
       libpq-dev libicu-dev libzip-dev libonig-dev \
       pkg-config autoconf make g++ \
  && docker-php-ext-configure intl \
  && docker-php-ext-install pdo pdo_pgsql mbstring intl zip opcache \
  && if [ -n "$HTTP_PROXY" ]; then pecl config-set http_proxy "$HTTP_PROXY"; fi \
  && pecl channel-update pecl.php.net || true \
  && (pecl install -o -f redis xdebug \
      || (echo "PECL direct install failed, trying tarballs" \
          && curl -fsSL --connect-timeout 20 --retry 5 ${HTTPS_PROXY:+--proxy "$HTTPS_PROXY"} https://pecl.php.net/get/redis-6.0.2.tgz -o /tmp/redis.tgz \
          && curl -fsSL --connect-timeout 20 --retry 5 ${HTTPS_PROXY:+--proxy "$HTTPS_PROXY"} https://pecl.php.net/get/xdebug-3.3.2.tgz -o /tmp/xdebug.tgz \
          && pecl install /tmp/redis.tgz /tmp/xdebug.tgz)) \
  && docker-php-ext-enable redis xdebug \
  && apt-get clean \
  && rm -rf /var/lib/apt/lists/*

# Composer
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer

WORKDIR /var/www/html

# Copy composer files first
COPY composer.json composer.lock ./

# Install PHP dependencies without running scripts (artisan not copied yet)
RUN composer install --no-dev --optimize-autoloader --no-scripts

# Copy application files (excluding vendor and node_modules)
COPY . .
RUN rm -rf node_modules

# Opcache recommended settings
ENV PHP_OPCACHE_VALIDATE_TIMESTAMPS=1 \
    PHP_OPCACHE_MAX_ACCELERATED_FILES=20000 \
    PHP_OPCACHE_MEMORY_CONSUMPTION=128 \
    PHP_OPCACHE_MAX_WASTED_PERCENTAGE=10

# Xdebug configuration (copied into conf.d)
COPY 99-xdebug.ini /usr/local/etc/php/conf.d/99-xdebug.ini

CMD ["php-fpm"]
nginx
# ./infra/nginx/default.conf
server {
    listen 80;
    server_name localhost;
    root /var/www/html/public;
    index index.php index.html;

    # PHP-FPM handler
    location ~ \.php$ {
        include /etc/nginx/fastcgi_params;
        fastcgi_pass backend:9000;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_index index.php;
    }

    # Frontend (Vue.js SPA) - все остальные запросы
    location / {
        proxy_pass http://frontend:80;
    }

    # Backend API
    location /api/ {
        try_files $uri $uri/ /index.php?$query_string;
    }

    client_max_body_size 20m;
}

# HTTPS server
server {
    listen 443 ssl;
    http2 on;
    server_name localhost;

    ssl_certificate /etc/nginx/certs/dev.crt;
    ssl_certificate_key /etc/nginx/certs/dev.key;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;

    index index.php index.html;

    # Frontend (Vue.js SPA) - все остальные запросы
    location / {
        proxy_pass http://frontend:80;
    }

    # Backend API - send directly to PHP without filesystem checks
    location /api/ {
         proxy_pass http://backend:9000;
    }

    client_max_body_size 20m;
}

Основная проблема в вашей конфигурации заключается в несоответствии путей между точкой монтирования в docker-compose.yml и корневой директорией в nginx‑конфигурации.

Ваша текущая конфигурация указывает nginx искать файлы в /var/www/html/public, но в контейнере бэкенда файлы монтируются в /var/www/html. Laravel требует, чтобы веб‑сервер обращался именно к директории public, а не к корню проекта.

bash
# docker-compose.yml
volumes:
  - ./backend:/var/www/html  # Файлы в /var/www/html

# nginx.conf  
root /var/www/html/public;  # Но nginx ищет в /var/www/html/public

Содержание


Основные причины 404 ошибок

1. Несоответствие путей монтирования

Как видно из вашего docker-compose.yml, бэкенд монтируется в /var/www/html, но nginx пытается обслуживать запросы из /var/www/html/public. Laravel требует, чтобы веб‑сервер обращался именно к директории public.

According to Stack Overflow discussions, это самая распространенная причина 404 ошибок в Docker‑конфигурациях Laravel.

2. Неправильная конфигурация try_files

Ваш HTTPS‑сервер не имеет корневой директории, что вызывает проблемы с обработкой статических файлов.

3. Отсутствие правильной обработки PHP‑файлов

Конфигурация location для PHP‑файлов может быть неполной для корректной работы с Laravel.


Исправление конфигурации

1. docker-compose.yml

Убедитесь, что корневая директория Laravel монтируется правильно:

yaml
backend:
  volumes:
    - ./backend:/var/www/html  # Правильно

2. nginx default.conf

Добавьте корневую директорию для HTTPS‑сервера и исправьте конфигурацию:

nginx
# HTTP сервер
server {
    listen 80;
    server_name localhost;
    root /var/www/html/public;  # Указываем правильную директорию
    index index.php index.html;

    # Frontend (Vue.js SPA)
    location / {
        proxy_pass http://frontend:80;
    }

    # Backend API - Laravel маршрутизация
    location /api/ {
        try_files $uri $uri/ /index.php?$query_string;
    }

    # Обработка PHP файлов
    location ~ \.php$ {
        include /etc/nginx/fastcgi_params;
        fastcgi_pass backend:9000;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_index index.php;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }

    client_max_body_size 20m;
}

# HTTPS сервер
server {
    listen 443 ssl;
    http2 on;
    server_name localhost;
    root /var/www/html/public;  # ДОБАВЛЕНО корневая директория
    index index.php index.html;

    # Frontend (Vue.js SPA)
    location / {
        proxy_pass http://frontend:80;
    }

    # Backend API - Laravel маршрутизация
    location /api/ {
        try_files $uri $uri/ /index.php?$query_string;
    }

    # Обработка PHP файлов
    location ~ \.php$ {
        include /etc/nginx/fastcgi_params;
        fastcgi_pass backend:9000;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_index index.php;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }

    client_max_body_size 20m;
}

Настройка nginx для Laravel

Правильная конфигурация location для PHP

Согласно исследованиям Laravel.io, для корректной работы Laravel с nginx необходима следующая конфигурация:

nginx
location ~ \.php$ {
    try_files $uri =404;
    fastcgi_split_path_info ^(.+\.php)(/.+)$;
    fastcgi_pass backend:9000;
    fastcgi_index index.php;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_param PATH_INFO $fastcgi_path_info;
}

Конфигурация try_files для API

Для корректной обработки API‑запросов в Laravel используйте:

nginx
location /api/ {
    try_files $uri $uri/ /index.php?$query_string;
}

Проверка прав доступа

Убедитесь, что веб‑сервер имеет правильные права доступа к директориям Laravel:

bash
# В контейнере backend
docker exec jdasia-backend chown -R www-data:www-data /var/www/html
docker exec jdasia-backend chmod -R 755 /var/www/html/storage
docker exec jdasia-backend chmod -R 755 /var/www/html/bootstrap/cache

Отладка и диагностика

1. Проверка логов

Проверьте логи nginx и PHP‑FPM:

bash
# Логи nginx
docker logs jdasia-nginx

# Логи PHP‑FPM  
docker logs jdasia-backend

# Логи ошибок PHP‑FPM
docker exec jdasia-backend tail /var/log/php8.2-fpm.log

2. Проверка существования файлов

Убедитесь, что файлы существуют в правильных директориях:

bash
# Проверьте содержимое контейнера backend
docker exec jdasia-backend ls -la /var/www/html/

# Проверьте наличие public директории
docker exec jdasia-backend ls -la /var/www/html/public/

3. Проверка прав доступа к файлам

bash
docker exec jdasia-backend ls -la /var/www/html/public/index.php

According to Server Fault, часто проблема заключается в том, что nginx не может найти файлы, которые существуют в PHP‑контейнере, но не смонтированы в nginx‑контейнер.


Источники

  1. Laravel docker-compose 404 not found Nginx - Stack Overflow
  2. Laravel, Docker and Nginx: 404 for all files in /public folder - Stack Overflow
  3. Nginx Laravel routing shows 404 - Laravel.io
  4. 404 Not Found - Nginx - docker compose - Unix & Linux Stack Exchange

Заключение

Основная проблема в вашей конфигурации — несоответствие путей между монтированием в docker-compose.yml и корневой директорией в nginx. Для решения:

  1. Убедитесь, что nginx root указывает на /var/www/html/public.
  2. Добавьте корневую директорию для HTTPS‑сервера.
  3. Проверьте права доступа к файлам.
  4. Используйте правильную конфигурацию try_files для Laravel.
  5. Настройте правильную обработку PHP‑файлов с fastcgi_param SCRIPT_FILENAME.

После этих изменений все запросы к /api/* должны корректно обрабатываться вашим Laravel бэкендом.

Авторы
Проверено модерацией
Модерация