Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

DaveG_'s avatar

NGINX and Laravel always returns response code 200

I sincerely hope you can help me as none of the similar topics and provided solutions fixed the situation.

I am running on the same container for simplicity nginx as reverse proxy, vue app fro frontend and laravel backend.

Nginx always returns status code 200, although I tested with a /test route that throws NotFoundException. The returned resp has header status 200 and upon inspection of the Response in the browser indeed 404 not found is displayed.

WHAT I HAVE ALREADY TRIED: Adding to pool php_admin_value[display_errors] = Off In my case it was not located in the default folder within the container but in /usr/local/etc/php/php-fpm.d/www.conf

Adding to nginx.conf error_page 404 /index.php;

No joy, still receiving 200 regardless of actual response.

MORE DETAILS ON THE CONFIGURATION: Dockerfile

FROM php:8.0-fpm

WORKDIR /var/www/html/app

# Install system dependencies and PHP extensions
RUN apt-get update && apt-get install -y nginx libpng-dev libjpeg-dev libfreetype6-dev zip unzip git \
    && docker-php-ext-configure gd --with-freetype --with-jpeg \
    && docker-php-ext-install gd pdo pdo_mysql

# Install Composer globally
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
RUN curl -sL 'https://deb.nodesource.com/setup_16.x' | bash - \
    && apt-get install -y nodejs

COPY ./api ./api
COPY ./frontend ./frontend
COPY ./nginx /etc/nginx/conf.d
COPY ./fpm /usr/local/etc/php-fpm.d   --->overriding the www.conf to add display_errors line


CMD ["php-fpm"]
EXPOSE 80

docker-compose.yml

version: "3"

services:
  web:
    build: ./
    restart: always
    volumes:
      - ./nginx:/etc/nginx/conf.d
      - ./api:/var/www/html/app/api
      - ./frontend:/var/www/html/app/frontend
#      - ./fpm/www.conf:/usr/local/etc/php-fpm.d  --->mount the custom www.conf for hot-reload

    ports:
      - "80:80"
  #    command: ['./frontend/start.sh']
    networks:
      - app

  mysql:
    image: 'mysql:8.0'
    volumes:
      - 'db_data:/var/lib/mysql'
    ports:
      - "3306:3306"
    environment:
      MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}'
      MYSQL_DATABASE: '${DB_DATABASE}'
      MYSQL_PASSWORD: '${DB_PASSWORD}'
      MYSQL_ALLOW_EMPTY_PASSWORD: 'yes'
    networks:
      - app

  mailhog:
    image: 'mailhog/mailhog:latest'
    ports:
      - 1025:1025
      - 8025:8025
    networks:
      - app
volumes:
  db_data:
    driver: local

networks:
  app:
    driver: bridge

nginx.conf

    upstream frontend {
        server localhost:3000;
    }

    upstream api {
        server localhost:9000;
    }

    server {
        listen 80;
        server_name localhost;

        error_log stderr;
        error_log /var/log/nginx/error.log;

        access_log stdout;
        access_log /var/log/nginx/access.log;

        location / {
#             root /var/www/html/app/frontend/dist;
            proxy_pass http://frontend;
            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  $scheme;
            proxy_read_timeout          1m;
            proxy_connect_timeout       1m;
            proxy_redirect off;
        }

        #localhost/storage should serve static files
        location /storage {
            #testing only
            return 200 "document_root: $document_root, request_uri: $request_uri, real_path_root: $realpath_root";
        }

        location /api/v1 {
            error_page 404 /index.php;

            root /var/www/html/app/api/public;
            include fastcgi_params;
            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 $scheme;

            fastcgi_buffers 16 16k;
            fastcgi_buffer_size 32k;
            fastcgi_intercept_errors on;
            fastcgi_pass 127.0.0.1:9000;
            fastcgi_param SCRIPT_FILENAME $document_root/index.php;
            fastcgi_param DOCUMENT_ROOT $realpath_root;
        }

        # Deny .ht* access
        location ~ /\.ht {
                deny all;
        }
    }

0 likes
4 replies
DaveG_'s avatar

Quick update: Attempted to serve with Apache instead of NGINX and got same results. Regardless if the api returns a successful response or throws an error the resp status is 200. Any ideas?

Quick update2: Just noticed the response headers are wrong too, they are set to text/html instead of application/json. Attempting to override them via response()->json()->header() did not change the response headers.

DaveG_'s avatar

@Tray2 Appreciate your reply, but this does not help, since the issue persists with apache as well, yet all requests are properly routed to the Laravel app and corresponding controller.

DaveG_'s avatar

TLDR issue resolution: configuring the FPM pool to not display errors did NOT work, however manually overriding php.ini and setting the follwowing lines worked:

php_admin_value[display_errors] = off
php_flag[display_errors] = off
php_admin_value[error_log] = /var/log/fpm-php/error.log
php_admin_flag[log_errors] = on
catch_workers_output = yes

Full explanation: The official php8.0-fpm did not have a php.ini file at all. instead it had 2 files called php-development.ini and php-production.ini in the following location: /usr/local/etc/php. I copied the dev ini and added the above lines, thus creating custom configuration. Then in the Dockerfile copied from local to container. Voila, Nginx no slaps 200 status on every response.

Please or to participate in this conversation.