Fix 404 Route Errors for CodeCanyon PHP Scripts on Nginx

by Fahim

You unpack a fresh PHP script from CodeCanyon, point your domain at the directory, and the landing page renders fine. But the second you click “Sign In”, hit /install, or ping an API endpoint, Nginx throws a cold 404 Not Found error. Here is how to fix your Nginx server block with the right try_files directive and FastCGI handler so routes actually hit the script’s entry point.

I ran into this exact headache setting up a multi-vendor booking platform for a client. The landing page worked without a hitch, but clicking anything else gave me Nginx’s default white 404 screen. The core issue is simple: almost every author on CodeCanyon develops and tests exclusively on Apache with .htaccess files.

Linux terminal displaying Nginx try_files configuration and FastCGI settings for PHP
Linux terminal displaying Nginx try_files configuration and FastCGI settings for PHP

Why CodeCanyon Scripts Fail on Nginx Out of the Box

Most commercial scripts bundle an Apache .htaccess file in their root. Apache reads it on every incoming request and uses mod_rewrite to route dynamic paths—like /login or /api/v1/orders—straight through to index.php.

Nginx ignores .htaccess files completely. By design, it handles all routing centrally inside virtual host configs under /etc/nginx/sites-available/.

When you visit example.com/login without an explicit rewrite rule, Nginx looks on your disk for a physical directory or file named /var/www/myscript/login. It doesn’t exist, so Nginx immediately returns a 404 without even checking in with PHP-FPM.

Find the Real Document Root

Before editing any server block, check where the script’s front controller actually lives. The most common mistake here is pointing Nginx to the project’s root folder instead of its public web root.

Modern PHP apps (built with Laravel, Symfony, or modern CodeIgniter) isolate application logic outside public reach. If you see directories like app, bootstrap, config, and a public or public_html folder, your web root must point directly inside that public directory.

Check the project structure over SSH:

ls -la /var/www/codecanyon-script

If index.php sits inside public, set your Nginx document root to /var/www/codecanyon-script/public. Leaving it at /var/www/codecanyon-script exposes your .env file (and all your database credentials) to the open web, while simultaneously breaking your static asset paths.

The Universal Nginx try_files Directive

To pass every incoming URI to the script’s front controller, you need the Nginx try_files directive. It tells Nginx: look for a matching file first; if there isn’t one, check for a matching folder; if both fail, hand the URI over to index.php along with the query string.

Open your site config:

sudo nano /etc/nginx/sites-available/my-script.conf

Inside the server block, make sure your location / matches this:

server { listen 80; server_name app.example.com; root /var/www/codecanyon-script/public; index index.php index.html; location / { try_files $uri $uri/ /index.php?$query_string; }
}

Don’t skip $query_string. That argument ensures GET requests like /search?keyword=hotel&page=2 keep their parameters when PHP takes over. If you drop it, pagination, search filters, and webhook callbacks will fail silently.

If you’re running this app on a dedicated subdomain, confirm your DNS is already resolving to the box. You can check how to point a Namecheap subdomain to a separate server before troubleshooting your routes further.

Set Up the PHP-FPM FastCGI Handler

Getting try_files right is step one. If your FastCGI configuration is off, Nginx will either serve the raw PHP code as a download or return a 500 error.

Check what PHP-FPM socket version is active on your server:

php -v
systemctl status php*-fpm

Ubuntu 22.04 or 24.04 usually runs PHP 8.1, 8.2, or 8.3. Add the matching FastCGI block right under location /:

location ~ .php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:/run/php/php8.2-fpm.sock; fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; include fastcgi_params;
}

I use $realpath_root here instead of $document_root. If you ever deploy via symlinks or automated deployment tools, $realpath_root resolves the true disk path and keeps PHP’s OPcache from getting out of sync.

If you run PHP through a local TCP port like 127.0.0.1:9000 instead of a UNIX socket, refer to the official PHP-FPM documentation for socket setup details.

Full Working Nginx Server Block

Here is a complete, battle-tested server configuration that handles 95% of modern Laravel-, Symfony-, and CodeIgniter-based CodeCanyon apps, with gzip compression and static caching included:

server { listen 80; listen [::]:80; server_name app.example.com; root /var/www/codecanyon-script/public; index index.php index.html; charset utf-8; # Logging access_log /var/log/nginx/codecanyon_access.log; error_log /var/log/nginx/codecanyon_error.log error; # Max upload size for script media/plugins client_max_body_size 64M; location / { try_files $uri $uri/ /index.php?$query_string; } location = /favicon.ico { access_log off; log_not_found off; } location = /robots.txt { access_log off; log_not_found off; } location ~ .php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:/run/php/php8.2-fpm.sock; fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; include fastcgi_params; fastcgi_hide_header X-Powered-By; } # Block access to hidden files like .env and .git location ~ /.(?!well-known).* { deny all; } # Cache assets location ~* .(jpg|jpeg|png|gif|ico|css|js|woff2|woff|ttf|svg)$ { expires 30d; add_header Cache-Control "public, no-transform"; access_log off; }
}

If the script allows user uploads, watch out for file size limits. If you hit upload ceilings right away, see our guide on how to fix 413 Request Entity Too Large on Nginx to adjust your buffers properly.

Handling Older Scripts (CodeIgniter & Custom PHP MVCs)

Not every CodeCanyon item runs on Laravel. Older scripts often rely on CodeIgniter 3, custom homegrown MVCs, or legacy procedural code that parses routes via PATH_INFO.

If your routes still throw 404s or blank screens after applying standard try_files, the script probably expects the URL passed inside the PATH_INFO variable.

Here is how to support legacy routing without hacking the script’s source code:

location / { try_files $uri $uri/ /index.php$is_args$args;
} location ~ ^(.+.php)(.*)$ { fastcgi_split_path_info ^(.+.php)(.*)$; fastcgi_pass unix:/run/php/php8.2-fpm.sock; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param PATH_INFO $fastcgi_path_info; fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info; include fastcgi_params;
}

The fastcgi_split_path_info directive isolates index.php from the virtual URI (like /admin/settings), allowing CodeIgniter’s $config['uri_protocol'] to grab the right route.

Fix Linux Permissions on Storage and Cache Directories

Once routing starts working, scripts often die during installation with a blank screen or a generic 500 error. Nine times out of ten, this is file permissions: Nginx and PHP-FPM run under www-data, but you extracted the zip as root or your SSH user.

Assign proper ownership to the web server user:

sudo chown -R www-data:www-data /var/www/codecanyon-script
sudo find /var/www/codecanyon-script -type d -exec chmod 755 {} ;
sudo find /var/www/codecanyon-script -type f -exec chmod 644 {} ;

Make sure writable paths like storage, bootstrap/cache, and uploads have proper write permissions:

sudo chmod -R 775 /var/www/codecanyon-script/storage
sudo chmod -R 775 /var/www/codecanyon-script/bootstrap/cache

Restart PHP-FPM to clear any cached file handles:

sudo systemctl restart php8.2-fpm

Testing and Reloading Nginx

Never run a blind restart on Nginx. A single misplaced semicolon will take down every site on your server.

Test your syntax first:

sudo nginx -t

You should see:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Once it checks out, reload without dropping active requests:

sudo systemctl reload nginx

If you’re also setting up SSL on this domain, verify that your server block doesn’t block Let’s Encrypt challenges. See our walk-through on how to fix Certbot SSL auto-renewal failures on Nginx to keep renewals running smoothly.

Troubleshooting FastCGI and Route Issues

If you still get a 404 or a 502 Bad Gateway after reloading, inspect your Nginx error log directly. It tells you the exact disk path Nginx tried to look up:

sudo tail -n 50 /var/log/nginx/error.log

Watch out for these common errors:

  • “open() … failed (2: No such file or directory)”: Your root path is wrong—usually because you forgot to append /public.
  • “connect() to unix:/run/php/php8.2-fpm.sock failed (111: Connection refused)”: PHP-FPM isn’t running, or your config references the wrong socket version (e.g., pointing to php8.1-fpm.sock when php8.2-fpm is installed).
  • “Primary script unknown”: FastCGI got the request, but SCRIPT_FILENAME pointed to a missing file. Verify your $realpath_root$fastcgi_script_name matches your root directive.

Frequently Asked Questions

Can I convert an Apache .htaccess file to Nginx rules automatically?

Online converters exist, but they usually output messy, outdated directives. For standard PHP applications, you don’t need a direct translation. That single try_files $uri $uri/ /index.php?$query_string; directive replaces practically all Apache rewrite rules.

Why does the installer load, but subsequent steps show a 404?

During setup, the installer writes an .env file with an APP_URL. If that value defaults to http://localhost while you access the site via an IP or domain, all form actions and AJAX requests will shoot off to the wrong host.

Why am I seeing a 502 Bad Gateway instead of a 404?

A 502 means Nginx matched the route correctly, but PHP-FPM didn’t reply. Make sure the PHP-FPM service is actually active and your fastcgi_pass socket path matches what’s running in /run/php/.

Do I need to enable PATH_INFO for Laravel scripts?

No. Laravel handles route parsing through standard query strings and front-controller rewrites. You only need explicit PATH_INFO handling for older CodeIgniter apps or custom vanilla MVCs.

Next Steps

Now that your CodeCanyon app routes are working cleanly, get your SSL certificate configured. If you have an external certificate package, check out our guide on how to install a Namecheap SSL on Nginx by combining CRT and CA-Bundle.

all_in_one_marketing_tool