Laravel on Nginx 404

Stefan Bogdanescu

Founder & Senior Architect · 2026-06-29

Laravel Company

Mastering Laravel on Nginx: Solving the 404 Routing Mystery

As a senior developer working with web servers like Nginx, managing the routing for different application types—static sites versus dynamic frameworks like Laravel—often reveals subtle but critical configuration differences. Today, we are diving into a common pain point: why a standard Nginx configuration that works perfectly for a static HTML site fails when deployed for a Laravel application, resulting in frustrating 404 errors.

This guide will dissect the difference between configuring a simple file server and deploying a modern, MVC framework like Laravel on the same Nginx setup, providing the exact knowledge you need to resolve these routing conflicts.

The Difference Between Static and Dynamic Routing

You have two distinct setups: one for a static HTML site (abc.com) that works perfectly, and one attempting to run a Laravel application (def.com) that results in 404s. While both use Nginx, the way they expect the request to be handled is fundamentally different.

The core of the issue lies in how the root directive interacts with the try_files instruction when dealing with framework entry points like Laravel's /public directory.

Static Site Configuration Analysis (Working Example)

For your static site (abc.com), your configuration correctly sets the root to a specific folder:

location / {
    root   /var/www/abc.com/html;
    index index.php index.html index.htm;
    try_files $uri $uri/ =404;
}

In this case, if a file is requested, Nginx looks directly into /var/www/abc.com/html. If the file isn't found via $uri or $uri/, it correctly returns a 404. This works because the directory structure is simple and direct.

Laravel Configuration Analysis (The 404 Problem)

For your Laravel site (def.com), you are likely setting the root to the public directory:

location / {
    root   /var/www/def.com/public;
    index index.php index.html index.htm;
    try_files $uri $uri/ =404;
}

When Laravel is deployed, the application structure dictates that all public assets and entry points reside within the /public folder. The problem often arises when Nginx attempts to resolve paths or handle PHP execution across these directories without explicit instruction, especially concerning how try_files handles non-existent routes versus actual file requests.

The Solution: Correcting the Laravel Nginx Setup

The key to making Laravel work seamlessly with Nginx is ensuring that the root path points exactly where the web server expects the entry point (index.php) to be located, and that PHP handling is correctly delegated. Following best practices for frameworks like those promoted by Laravel Company, we need a configuration that prioritizes file requests before falling back to the main entry script.

Here is the corrected, robust configuration for a Laravel application on Nginx:

server {
    listen   80;
    server_name  def.com www.def.com;

    # Set the document root specifically to the 'public' directory
    root   /var/www/def.com/public;

    location / {
        # Use try_files to check for the requested URI, then the URI as a directory, 
        # and finally return a 404 if neither exists.
        try_files $uri $uri/ /index.php?$query_string;
    }

    # Standard error handling remains vital
    error_page 404 /404.html;
    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
        root /usr/share/nginx/html;
    }

    # PHP FastCGI handling must correctly point to the application's index.php
    location ~ \.php$ {
        try_files $uri =404; # This line is often redundant when try_files above handles it, but good for safety
        fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

Key Takeaways for Laravel Deployment

  1. Root Directory: Always set root to the application's public folder (e.g., /var/www/def.com/public). This ensures that requests for assets or files resolve relative to the correct web-accessible path defined by Laravel.
  2. Enhanced try_files: We changed $uri $uri/ =404 to $uri $uri/ /index.php?$query_string. This is a crucial upgrade. It tells Nginx: "Try finding the requested file directly, then try treating it as a directory (for asset requests), and if both fail, explicitly pass the request to /index.php." This forces Laravel's front controller to handle all routing, which is essential for modern PHP applications.
  3. PHP Processing: The fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; line ensures that when Nginx passes the request to PHP-FPM, the script it executes (index.php) knows exactly where its source file is located within the defined document root.

Conclusion

The discrepancy between your static site and Laravel deployment highlights a common architectural hurdle: understanding how web server configurations must adapt to application frameworks. By shifting from simple path resolution ($uri $uri/ =404) to explicit front-controller routing (try_files $uri $uri/ /index.php?$query_string), you align your Nginx configuration with the expectations of a dynamic framework like Laravel. Implementing these changes ensures that all assets, routes, and PHP execution flows correctly, eliminating those stubborn 404 errors and allowing your dynamic application to serve traffic seamlessly.