ryoluo/sail-ssl
ryoluo/sail-ssl adds HTTPS to Laravel Sail with minimal setup: generate locally trusted SSL certificates and serve your app over https://localhost. Useful for testing secure cookies, service workers, OAuth callbacks, and other HTTPS-only features during local Laravel development.
sail-ssl wraps a standard Laravel Sail project in an nginx TLS terminator plus a local certificate authority, so https://localhost (and any custom *.test hostnames) works in the browser without warnings. v1.5.0 is the first release that is genuinely comfortable to adopt: it understands Sail's new compose.yaml layout, edits the compose file with a real YAML parser instead of regex, and signs leaf certificates from a local two-tier CA rather than shipping a naked self-signed cert.
Minimal path on a fresh app:
php artisan sail:install) and confirm sail up -d serves http://localhost before adding TLS on top. Debugging two moving parts at once is the most common way to lose an afternoon.composer require ryoluo/sail-ssl --dev.php artisan sail-ssl:install. Run php artisan list | grep sail-ssl first — the package exposes a small command surface and the installer's flags (alternate names, force/regenerate) are where the useful knobs live.sail up -d and hit https://localhost.Where to look first when something is off: the generated compose.override.yaml (or whatever file the installer reports writing), the nginx template under docker/, and the cert directory the install command prints. Those three files are the entire system.
First real use case: a team-standard local domain, e.g. set SSL_ALT_NAME=myapp.test and add 127.0.0.1 myapp.test to /etc/hosts, then point APP_URL=https://myapp.test. That is the configuration you will actually keep; localhost is just the smoke test.
Bootstrap order in a project template. If you maintain a starter kit, run sail:install then sail-ssl:install then commit the generated override file and the nginx config, but not the certificates. New clones then only need "generate certs + trust CA", which is one command and one Keychain click.
Per-developer certs, shared config. Because v1.5.0 issues from a local CA, each developer gets their own root. Keep certs out of git and document the trust step in the README; treat CA trust as onboarding, not as a repo artifact. If you do want a shared CA (useful for a stable CI image), commit the CA key/cert to a private, dev-only location and be explicit that it must never be used outside local development.
Multiple SANs instead of multiple projects. Rather than re-running the installer per app, list every hostname you need in SSL_ALT_NAME (space- or comma-separated depending on the installer's parsing — check the generated openssl/config output to confirm what actually landed in the SAN extension). Wildcards like *.myapp.test cover subdomain-per-tenant setups.
Dusk and HTTP tests over HTTPS. For browser tests, either pre-trust the CA inside the test container or launch with --ignore-certificate-errors; for $this->get(...) style tests, withoutVerifying() is the pragmatic route. Assert on https:// explicitly — a surprising number of apps silently keep APP_URL=http://localhost and tests never exercise the TLS path.
Vite/HMR. TLS on the PHP side does not make the Vite dev server HTTPS. If your vite.config.js uses server.https, point it at the same cert files, and make sure those files are readable from the Node container (they're usually generated on the host with host-only permissions). Mismatched schemes here produce mixed-content console noise that looks like a cert problem but isn't.
Extending the proxy. Publish/override the nginx template for things Sail's default doesn't cover: client_max_body_size for uploads, HTTP→HTTPS redirects, gzip/brotli, or a second upstream. Keep these edits in the template, not by hand-editing the generated compose file, so a re-install doesn't silently drop them.
Port discipline. If several projects run simultaneously, don't fight over 80/443 — map the proxy to distinct host ports and accept the :8443 in the URL, or run one shared reverse proxy in front of all of them.
/usr/local/share/ca-certificates and run update-ca-certificates. Firefox keeps its own store and needs a separate import. Chrome on macOS reads the Keychain; Safari caches aggressively — quit and reopen before concluding the cert is wrong.symfony/yaml, which is a big correctness win but means comments, anchors, merge keys, and custom ordering in compose.override.yaml will be flattened on every run. Treat that file as generated output. Put anything you care about in a sibling override file that the tool doesn't touch.compose.yaml; older Sail installs use docker-compose.yml. If sail up ignores your TLS changes entirely, check which filename the installer wrote versus which one your Sail version reads — a silent no-op is the failure mode here, not an error.SSL_ALT_NAME is now safe (PR #38 fixed a cert-generation crash). Still, distinguish "unset" from "set to empty string" in .env; if you rely on defaults, leave the line out rather than setting SSL_ALT_NAME=.symfony/yaml. Laravel apps that pin Symfony components can hit a constraint conflict. composer why symfony/yaml after install; the required major should track your framework version.docker compose config to confirm the override actually merged; openssl x509 -in <cert>.crt -text -noout to read the SAN list and validity dates; curl -vk https://localhost from the host; openssl s_client -connect localhost:443 -servername myapp.test when SNI routing is in play. Most "certificate invalid" reports are a missing SAN or the wrong hostname, not a broken chain.Strict-Transport-Security or a permanent redirect for localhost, the browser will keep forcing HTTPS even after you tear the proxy down. Clear it via chrome://net-internals/#hsts before blaming the package.root on bind mounts, which breaks later host-side regeneration and makes Vite unable to read them. Generate on the host where possible, or chown afterwards.How can I help you explore Laravel packages today?