How Nginx and PHP-FPM turn a web request into code

Let's see how an HTTP request gets from your web server into your PHP/Laravel code base.

The moving parts are:

  1. Nginx - receives web request, sends it to PHP-FPM via FastCGI
  2. PHP-FPM - Takes FastCGI request, spins up processes of PHP and runs your code
  3. Laravel - Takes PHP super globals, along with php://input stream, and creates an HTTP Request class

Nginx

Nginx is configured for Laravel/PHP applications with the following config:

server {
    server_name app.chipperci.com;
    root /home/forge/app.chipperci.com/public;

    index index.html index.htm index.php;

    charset utf-8;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    error_page 404 /index.php;

    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
        fastcgi_index index.php;
        include fastcgi_params;
    }
}

This is taken from Laravel Forge, with a bunch of items removed for brevity. What we see above are the parts we care about.

The first location / {} uses try_files to attempt to match the request URI to a static file or directory within the configured root, which is /home/forge/app.chipperci.com/public in our example. If it files a static file, it serves it! Otherwise, it runs index.php.

If the file found ends in .php (or we're using the fallback index.php file), then we eventually end up in the location ~ \.php$ {} block. This passes the request off to PHP-FPM via the FastCGI protocol.

It first splits the path at whatever *.php file is in there. This lets PHP get the correct URI of the request - everything AFTER index.php usually.

We also include fastcgi_params which is the information that is populated in the $_SERVER PHP super global.

Finally we pass the request off to PHP-FPM, which in this case is listening via a unix socket file at /var/run/php/php8.2-fpm.sock.

PHP-FPM

PHP-FPM is written in C, and is hard to parse (in other words, I have no idea what's going on in there). A lot of the logic to taking a request and spinning up PHP is in the fpm_request.c file. However the basics are that PHP-FPM manages processes and runs our PHP application through its "entrypoint", the index.php file.

PHP-FPM populates the PHP super globals, any any streams needed such as php://input (that will be the body of the HTTP request if there is one).

Our Code

Your framework likely abstracts the HTTP request into a class that represents the HTTP request itself. In Laravel, that's this class, which extends the underlying Symfony HTTP request here.

Laravel uses the method createFromGlobals(), which creates an HTTP request instance based off of the global information (super globals) and gets the body of the HTTP request from php://input.

Then Laravel can use that to match against registered routes, run controller code, and generally do all the magic it does for us!