Warning: You are reading an outdated version of this guide. Unless you're here for a specific reason, you probably want the updated version instead.
This article shows how to configure Nginx for serving a Laravel web app. We’ll be using Laravel’s example Nginx config—it works well in my experience and I see no reason to reinvent the wheel here. This article is basically walking through Laravel’s example Nginx config with short explanations of what each line does.
Your VPS might be using Apache as the default web server. If so, you should disable Apache to avoid conflicts with Nginx.
# Stop and disable Apache
laravel@server$ sudo systemctl stop apache2.service
laravel@server$ sudo systemctl disable apache2.service
You might also want to uninstall Apache entirely, since you probably won’t need it going forward now that you are using Nginx:
# Remove all Apache-related packages
laravel@server$ sudo apt purge apache2*
laravel@server$ sudo apt autoremove
You can then install and start Nginx:
# Install, enable, and start Nginx
laravel@server$ sudo apt install nginx
laravel@server$ sudo systemctl enable --now nginx.service
You can then test that Nginx is up and running by pasting your app’s IP address into a web browser’s address bar. You should see the default “Welcome to nginx!” page.
Create an Nginx config file for your Laravel app:
# Create an Nginx config for your Laravel app
laravel@server$ sudoedit /etc/nginx/sites-available/laravel-project
Two directories are relevant for Nginx configuration (at least on Debian and Ubuntu systems):
/etc/nginx/sites-available/
contains a dedicated config file for every website (or, more precisely, virtual host) hosted on your server, whether the site is currently enabled or not./etc/nginx/sites-enabled/
contains symlinks to files in the sites-available/
folder.The idea is to “activate” the site(s) you want Nginx to serve by creating a symlink in sites-enabled/
linking to the site’s config file in sites/available/
.
But there is a twist to the story…
The sites-available/
and sites-enabled/
convention is specific to the version of Nginx packaged specifically for Debian and Ubuntu systems (supposedly the goal is to parallel the a2ensite
/a2dissite
workflow used with Apache).
Upstream versions of Nginx prefer using the /etc/nginx/conf.d/
directory for configuring your virtual hosts, and indeed sites-enabled/
and sites-available/
is said to be deprecated in Chapter 1.5 of the Nginx cookbook.
I’m sticking with sites-enabled/
and sites-available/
in this guide, since that’s what you’ll find in most online guides and what you’ll probably have set up on your VPS out of the box.
See this StackOverflow thread for further reading.
Inside /etc/nginx/sites-available/laravel-project
paste Laravel’s example Nginx config (I’ve added some comments—feel free to remove them).
Make sure to update the server_name
and root
directives, and possibly the PHP version and/or file path in fastcgi_pass
.
The rest can be left as is.
server {
# Listen for connections on port 80 (HTTP)
listen 80;
# Add this if using IPv6 on your server
# listen [::]:80;
# IP addresses and domain names that should point to your app.
# Set this to your server's IP address (and/or domain name, if you've set up DNS).
server_name 1.2.3.4;
# server_name 1.2.3.4 example.com; # if using a domain name
# The root directory for incoming web requests.
# Set this to the full path to your Laravel app's `public` subdirectory,
# which is the entry point to Laravel applications.
root /srv/www/laravel-project/public;
# This sets the X-Frame-Options HTTP response header such that your site can
# be embedded in a frame only if the site including it is the same as the one
# serving the page.
# This supposedly serves to prevent click-jacking attacks; for details see
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
add_header X-Frame-Options "SAMEORIGIN";
# This sets the X-Content-Type-Options HTTP response header to help revent
# MIME type sniffing. For details see
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options
add_header X-Content-Type-Options "nosniff";
# Use PHP (and not HTML) index files (because Laravel is PHP-based).
index index.php;
# Specify UTF-8 character encoding in Content-Type HTTP response header fields.
charset utf-8;
# The order recommended by Laravel for Nginx to check for the existence of
# files based on a URI or query string.
location / {
try_files $uri $uri/ /index.php?$query_string;
}
# Disable logging related to favicon and robots.txt files.
location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }
# Redirect to the home page (i.e. /index.php) on 404 errors.
error_page 404 /index.php;
# Laravel's recommended settings for handling requests for PHP files.
location ~ \.php$ {
# The `fastcgi_pass` directive should point to the address of the FastCGI
# Process Manager (FPM) on your server. The value below should be correct,
# but you might need to tweak the PHP version (from e.g. 8.1 to 8.2)
# depending on the PHP version installed on your server
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
# Denies all attempts to access hidden files that are not associated with
# well-known services. See e.g. https://en.wikipedia.org/wiki/Well-known_URI
location ~ /\.(?!well-known).* {
deny all;
}
}
Here is the official documentation of Nginx directives, if you’re interested.
Create a symlink to enable your Laravel site:
# Create a symlink enabling your Laravel app
laravel@server$ cd /etc/nginx/sites-enabled
laravel@server:sites-enabled$ sudo ln -s ../sites-available/laravel-project laravel-project
# Remove the active link to the default Nginx splash page
laravel@server:sites-enabled$ sudo rm default
Then test the syntax of the active Nginx config file:
# Test syntax of Nginx config file for errors/misconfiguration
laravel@server$ sudo nginx -t
Assuming the test succeeded, you can restart Nginx.
# Restart Nginx
laravel@server$ sudo systemctl restart nginx.service
The new sites-enabled
link will take effect after restarting Nginx, and Nginx will begin serving your Laravel application.
Point a web browser to your server’s IP address. Assuming you’ve correctly followed the steps so far, your web app should be live and working properly.
There are, of course, a lot of moving parts here—far more than I could fully cover—but here are two common problems:
An error along the lines of “File not found” means that Nginx cannot find your app’s root directory, and you’ll have to fix something with your Nginx setup.
At the risk of being annoying, read through this article and double-check you’ve followed each step—ensure you’ve removed Apache, that all paths and addresses in your Nginx config are correct, and that your app’s Nginx config is properly symlinked into place in sites-enabled
.
A “HTTP 500 Internal Server Error” means that Nginx is properly set up and serving your Laravel app, but that there is a problem with the app itself.
The best advice I can offer (besides the annoying suggestion to double check the past few articles) is to temporarily reenable debug mode to help diagnose the problem:
.env
file.APP_DEBUG=true
and exit the .env
file.php artisan config:cache
to update your config cache.Common problems include errors when appending to your application log (you’ll need to give the www-data
group write permissions on your app’s storage/log
directory—revisit the permissions article) or failure to connect to your database (make sure the database connection settings in your app’s .env
file (covered in the environment configuration article) match the database created in the database article).
Even if these common issues don’t apply to you, Laravel’s debug messages can hopefully give you a lead. In any case, you should disable debug mode when you diagnose and fix the problem.
And if you’re sure you’ve exactly followed the guide so far and your app is still down, please let me know—I’ve done my best to battle-test this guide and triple-check everything works, but there could still be mistakes or unexpected failure modes, which I would want to address.
Next: The next article covers automated redeployment of your Laravel app.
Finding this tutorial series useful? Consider saying thank you!
The original writing and media in this series is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.