Fixing “Primary script unknown” Error in Nginx+PHP-FPM: Symfony Routing and Static File Serving Configuration


4 views

When setting up a Symfony application with Nginx and PHP-FPM in Docker, a common stumbling block is the "Primary script unknown" error. This typically occurs when Nginx fails to properly communicate with PHP-FPM about the script location, particularly in complex routing scenarios where you need to:

  • Serve static files from one directory (/public)
  • Route API requests to a Symfony front controller (/api/index.php)

The original configuration has several issues that trigger this error:

# Problematic original config
location /api {
  root /usr/share/nginx/html/api;  # This changes root context
  try_files $uri /index.php;       # Doesn't properly handle PHP requests
}

Here's the working solution that properly handles both static files and Symfony routing:

upstream php {
  server php:9000;
}

server {
  listen 80 default_server;
  server_name impressive.local;
  root /usr/share/nginx/html/public;
  
  # API路由处理
  location /api {
    alias /usr/share/nginx/html/api/public;
    try_files $uri /index.php$is_args$args;
    
    location ~ \.php$ {
      fastcgi_pass php;
      include fastcgi_params;
      fastcgi_param SCRIPT_FILENAME $request_filename;
      fastcgi_param DOCUMENT_ROOT $realpath_root;
    }
  }

  # 静态文件处理
  location / {
    try_files $uri $uri/ =404;
  }

  # 全局PHP处理 (备选方案)
  location ~ \.php$ {
    return 404; # Explicitly block direct PHP access outside /api
  }
}

The critical adjustments that fixed the issue:

# Using alias instead of root for precise path mapping
alias /usr/share/nginx/html/api/public;

# Proper PHP-FPM parameters
fastcgi_param SCRIPT_FILENAME $request_filename;
fastcgi_param DOCUMENT_ROOT $realpath_root;

# Correct try_files syntax for Symfony
try_files $uri /index.php$is_args$args;

The docker-compose.yml also needs proper volume mappings:

version: '3'
services:
  web:
    image: nginx:alpine
    volumes:
      - ./web/public:/usr/share/nginx/html/public
      - ./web/api:/usr/share/nginx/html/api
      - ./nginx.conf:/etc/nginx/conf.d/default.conf
    ports:
      - "8080:80"
    depends_on:
      - php

  php:
    image: php:7.4-fpm-alpine
    volumes:
      - ./web/api:/var/www/api
      - ./web/public:/var/www/public

To test if everything works correctly:

  1. Access static files: http://localhost:8080/index.html
  2. Access API endpoint: http://localhost:8080/api/endpoint
  3. Check error logs: docker-compose logs -f php
  • Mixing root and alias directives incorrectly
  • Incorrect filesystem permissions (PHP-FPM needs read access)
  • Missing fastcgi_param configurations
  • Path mismatches between Docker containers

When configuring Nginx to route API requests to PHP-FPM while serving static content separately, we often encounter the "Primary script unknown" error. This occurs when PHP-FPM can't locate the script that Nginx is trying to execute.

The current setup has several issues:


# Current problematic location block
location /api {
  root /usr/share/nginx/html/api;
  try_files $uri /index.php;
}

The main problems are:

  1. Incorrect root directive placement
  2. Missing proper PHP handler for API routes
  3. Improper try_files configuration

Here's the corrected Nginx configuration:


upstream php {
  server php:9000;
}

server {
  listen 80 default_server;
  server_name impressive.local;
  index index.html index.php;
  root /usr/share/nginx/html/public;

  location /api {
    alias /usr/share/nginx/html/api;
    try_files $uri /api/index.php$is_args$args;
  }

  location ~ \\.php$ {
    fastcgi_pass php;
    fastcgi_split_path_info ^(.+\\.php)(/.*)$;
    include fastcgi_params;
    
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_param DOCUMENT_ROOT $realpath_root;
  }
}

1. Using alias instead of root: The alias directive is more appropriate when mapping URI prefixes to filesystem paths.

2. Proper try_files directive: The new version explicitly routes missing files to index.php with preserved query parameters.

3. Enhanced PHP handler: Added crucial fastcgi parameters to ensure proper script location resolution.

For Docker environments, ensure your volumes are properly mounted. Verify the paths match between Docker and Nginx configurations:


volumes:
  - ./web:/usr/share/nginx/html

Check the actual files exist in the container:


docker exec -it container_name ls -la /usr/share/nginx/html/api/

For Symfony applications, consider these additional optimizations:


location /api {
  alias /usr/share/nginx/html/api;
  try_files $uri /api/index.php$is_args$args;
  
  # Symfony specific additions
  fastcgi_param APP_ENV prod;
  fastcgi_param APP_DEBUG 0;
}

After making changes:


nginx -t
service nginx reload

Test the API endpoint:


curl -I http://localhost:8080/api/test
  • Permission issues: Ensure PHP-FPM worker has read access to the files
  • Path mismatches: Verify all paths are absolute and correct
  • Caching problems: Clear opcache if you're making frequent changes