How to setup Nginx to run PHP/Laravel API with frontend app on same domain

There are multiple ways of deploying this kind of applications and here I will describe my way of doing it. Lately, many applications separate frontend and backend parts so we can observe them as two projects. We could have Laravel API and Angular/React/Vue on the other side. If we have this project structure then our frontend (eg. Vue) is purely static app.

Running npm run build is going to generate dist directory which contains our entire application which we can then serve using Web server.

As for the PHP part, we want to use FastCGI Process Manager (FPM). This is a topic in itself but in short FPM will handle PHP files much more efficiently and thus improve performance and enable a higher load.

How PHP and Nginx work together (Image credit: DataDog)
How PHP and Nginx work together (Image credit: DataDog)

Many developers will serve frontend part on www.example.com and backend part on www.api-example.com. This is pretty simple to configure but when I wanted to serve Vue app on www.example.com and my API on www.example.com/api I ran into some problems. If you just bought server and don’t have a domain yet, this is the way you can deploy your app. After some time this is the configuration I decided to use

server {
    listen 80;
    server_name 147.242.153.77; # fake IP address
    root /home/myfolder/example/html; #path to static directory
    
    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";
    
    index index.html index.htm index.php;
    charset utf-8;
    
    location / {
            try_files $uri $uri/ /index.html;
    }
    
    location /api {
            alias /home/myfolder/example-api/public;
            try_files $uri $uri/ @laravelapi;
            location ~ \.php$ {
                    include snippets/fastcgi-php.conf;
                    fastcgi_pass unix:/var/run/php/php8.0-fpm.sock;
                    fastcgi_param SCRIPT_FILENAME $request_filename;
            }
    }

    location @laravelapi {
            rewrite /api/(.*)?$ /api/index.php?$is_args$args last;
    }

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }
    error_page 404 /index.php;

    location ~ /\.(?!well-known).* {
            deny all;
        }
}

We set root directive to our frontend app. Every request is going to hit our first location block if it doesn’t start with /api and it is going to execute index.html from Vue static directory.

Request made for /api/users is a little bit more complicated. It is going to hit second location block. Then we are using alias to set correct path to our API.

Note: alias directive doesn’t append the location part. If we used root instead it would try to access /home/myfolder/example-api/public/api and that is not what we want.

The try_files means when you receive a URI that’s matched by this block try $uri first, eg. /api/images/image.jpg , Nginx will try to serve image.jpg file inside /home/myfolder/example-api/public/images. Then it will try to serve whole folder /images.

Last option is a fallback option which will redirect request to the named location @laravelapi. Inside this block we will rewrite /api/users into /api/index.php.

Flag last stops processing the current set of ngx_http_rewrite_module directives and starts a search for a new location matching the changed URI.

Because of the new URI /api/index.php, this time we will finally hit regex location block ~ .php$ that is going to pass processing of the /home/myfolder/example-api/public/index.php script to FPM.

IMPORTANT: With this configuration our Laravel application is deployed on 147.242.153.77/api so we can remove route prefix /api from Laravel’s RouteServiceProvider.php . If we hadn’t done that we would need to send requests to /api/api/users to get a response, otherwise we would get 404.

Tags: No tags

Comments are closed.