# Laravel

URL: https://scalws.com/docs/howto/laravel/
Source: `docs/howto/laravel.md` in the ScalWS repository

Serve a Laravel application from its `public/` directory through PHP-FPM, with the Laravel
profile's deny and cache-bypass rules.

## Prerequisites

* ScalWS installed, with `include: sites.d/*.yaml` in `/etc/scalws/scalws.yaml` (see
  [Sites](https://scalws.com/docs/sites/)).
* The project deployed outside `/home`, for example `/srv/shop`, with `composer install`
  done and `.env` configured. Only `/srv/shop/public` is served.
* The project readable by group `scalws` (`sudo chmod -R g+rX /srv/shop` with group
  `scalws`).
* PHP-FPM: the distribution's pool under scalws-controller, or a pool ScalWS manages
  under `scalwsd.service` (see
  [the three services](https://scalws.com/docs/guide/installation/#the-three-services)).

Let ScalWS confirm the framework first; `detect` only reads the directory:

```sh
scalwsctl detect /srv/shop
```

## What the profile does

[`profile: laravel`](https://scalws.com/docs/reference/site-file/#profile) gives:

* a PHP runtime with root `<root>/public`, `front_controller: /index.php`, a managed pool
  of 2 to 16 workers and `memory_limit: 256M` (used only when the site has no `php:`);
* deny rules for `/.env`, `/storage/logs` and `/vendor` (answered `404`);
* cache-bypass rules for the `laravel_session` and `XSRF-TOKEN` cookies (used when `cache:`
  is set).

## Under scalws-controller: an existing PHP-FPM pool

The controller refuses managed pools, so give the pool and the public root yourself; the
profile's rules still apply:

```yaml
# /etc/scalws/sites.d/shop.example.com.yaml
domain: shop.example.com
root: /srv/shop
profile: laravel
php:
  fastcgi: unix:/run/php/php8.3-fpm.sock
  root: /srv/shop/public
  front_controller: /index.php
https_redirect: true
```

* [`root`](https://scalws.com/docs/reference/site-file/#root) is the project directory.
* [`php.root`](https://scalws.com/docs/reference/site-file/#php.root) is the document root: never serve the
  project root.
* [`php.front_controller`](https://scalws.com/docs/reference/site-file/#php.front_controller) sends every
  path that is not a file to `public/index.php`.

User `scalws` must be able to connect to the pool's socket (on Debian and Ubuntu:
`sudo usermod -aG www-data scalws`, then `sudo systemctl restart scalws-controller` and
`sudo scalws-controller reload`). PHP-FPM's pool user needs write access to `storage/` and
`bootstrap/cache/`.

## Under scalwsd.service: a managed pool

The profile supplies the whole runtime:

```yaml
# /etc/scalws/sites.d/shop.example.com.yaml
domain: shop.example.com
root: /srv/shop
profile: laravel
https_redirect: true
```

Or written out, to change the pool:

```yaml
domain: shop.example.com
root: /srv/shop
profile: laravel
php:
  root: /srv/shop/public
  front_controller: /index.php
  fpm:
    workers: {min: 4, max: 16}
    ini: {memory_limit: 256M}
    env: {APP_ENV: production}
```

The pool clears the environment and passes only
[`php.fpm.env`](https://scalws.com/docs/reference/site-file/#php.fpm.env). PHP runs as `scalws` (or the
tenant's `user`) inside the unit's sandbox, where `/srv` is read-only. Allow Laravel's
writable directories with a drop-in:

```ini
# /etc/systemd/system/scalwsd.service.d/writable.conf
[Service]
ReadWritePaths=/srv/shop/storage /srv/shop/bootstrap/cache
```

Then `sudo systemctl daemon-reload && sudo systemctl restart scalwsd`.

## Apply

```sh
sudo scalwsctl check
sudo scalws-controller reload        # under scalws-controller
sudo scalwsctl reload                # under scalwsd.service
```

## Check it works

```sh
curl -sI -H 'Host: shop.example.com' http://127.0.0.1/          # 200 from public/index.php
curl -sI -H 'Host: shop.example.com' http://127.0.0.1/.env      # 404
curl -sI -H 'Host: shop.example.com' http://127.0.0.1/up        # Laravel 11 health route
sudo scalwsctl site list
```

## Common problems

* **The project's files are reachable**, for example `/composer.json`: the PHP root is the
  project root. Set `php.root` to `public/` (or use the profile without `php:`).
* **`managed php runtimes are not supported with several instances (ADR-0036)`**: under
  scalws-controller the site needs `php.fastcgi`.
* **`public/.htaccess` seems ignored**: with `htaccess: true`, `.htaccess` files are read
  below the site's `root` (`/srv/shop`), not below `php.root`. Laravel's file only routes
  to `index.php`, which `front_controller` already does, so you do not need it.
* **500 from Laravel, "Permission denied" in `storage/logs`**: the PHP user cannot write
  `storage/` or `bootstrap/cache/`.
* **502**: PHP-FPM is not running or its socket does not let `scalws` connect; see
  [troubleshooting](https://scalws.com/docs/guide/troubleshooting/#php-fpm).

See also: [applications](https://scalws.com/docs/guide/applications/#laravel),
[profiles](https://scalws.com/docs/guide/applications/#profiles-and-detection).
