After migrating from Laravel Sail to Herd on Mac I needed to also find a way to make websockets work, as this app used them. Previously I was relying on soketi and it was running under its own docker compose, but with Sail gone, this went too. Sure I could just run soketi from npm but since Laravel offers a first-class solution, a Laravel Reverb, I decided to try that.
After changing a few files and a few configs, my app could connect to the websocket, because I could see it in the browser Network tab or via:
php artisan reverb:start --debug
The problem was that the dispatched events were failing with the infamous error:
cURL error 60: SSL certificate problem: unable to get local issuer certificate
I have spent considerable time debugging this as no search or AI magic could point me to right direction, with Herd specifically. I had to get my hands dirty.
Laravel Herd php.ini
First I needed to make sure the php
binary used was in fact served by
Herd:
/Users/peterbabic/Library/Application Support/Herd/bin/php
The above file is a symlink to a proper binary:
ll /Users/peterbabic/Library/Application\ Support/Herd/bin/php
lrwxr-xr-x@ 1 peterbabic staff 61B Jul 30 09:18 /Users/peterbabic/Library/Application Support/Herd/bin/php@ -> /Users/peterbabic/Library/Application Support/Herd/bin//php84
Probing around Herd more, I could find a php.ini
related to the running
PHP 8.4 version:
cat /Users/peterbabic/Library/Application\ Support/Herd/config/php/84/php.ini
Returns these bits, the second line specifically is the one we are looking for:
curl.cainfo=/Users/peterbabic/Library/Application Support/Herd/config/php/cacert.pem
openssl.cafile=/Users/peterbabic/Library/Application Support/Herd/config/php/cacert.pem
pcre.jit=0
output_buffering=4096
memory_limit=128M
upload_max_filesize=2M
post_max_size=2M
However, when I tried to check if php binary is loading this setting, I could see it doesn't:
php -i | grep -i ssl
Specifically the line openssl.cafile => no value => no value
was the
culprit:
Registered Stream Socket Transports => tcp, udp, unix, udg, ssl, tls, tlsv1.0, tlsv1.1, tlsv1.2, tlsv1.3
SSL => Yes
MULTI_SSL => No
SSL Version => OpenSSL/3.5.1
SSL Support => enabled
libmongoc SSL => enabled
libmongoc SSL library => OpenSSL
core SSL => supported
extended SSL => supported
openssl
OpenSSL support => enabled
OpenSSL Library Version => OpenSSL 3.5.1 1 Jul 2025
OpenSSL Header Version => OpenSSL 3.5.1 1 Jul 2025
Openssl default config => /etc/ssl/openssl.cnf
openssl.cafile => no value => no value
openssl.capath => no value => no value
OpenSSL support => enabled
Of course the system is unable to load the certificate when it is not even instructed to do so!
The certificate
Probing around the Herd directory structures, I could clearly see the certificate was present:
stat /Users/peterbabic/Library/Application\ Support/Herd/config/php/cacert.pem
If for any reason it is not there, regenerate it easily by running
herd secure
which outputs the following and creates the file:
The [laravel.test] site has been secured with a fresh TLS certificate.
So why php was not loading its php.ini
file provided by Herd? Well, this
was a good thing to search for as it proved fruitful.
Setting up
For my fish shell setup I needed to add these variables into config.fish
so Herd could pick them up:
set -x HERD_PHP_85_INI_SCAN_DIR "/Users/peterbabic/Library/Application Support/Herd/config/php/85/"
set -x HERD_PHP_84_INI_SCAN_DIR "/Users/peterbabic/Library/Application Support/Herd/config/php/84/"
set -x HERD_PHP_83_INI_SCAN_DIR "/Users/peterbabic/Library/Application Support/Herd/config/php/83/"
set -x HERD_PHP_82_INI_SCAN_DIR "/Users/peterbabic/Library/Application Support/Herd/config/php/82/"
set -x HERD_PHP_81_INI_SCAN_DIR "/Users/peterbabic/Library/Application Support/Herd/config/php/81/"
set -x HERD_PHP_80_INI_SCAN_DIR "/Users/peterbabic/Library/Application Support/Herd/config/php/80/"
set -x HERD_PHP_74_INI_SCAN_DIR "/Users/peterbabic/Library/Application Support/Herd/config/php/74/"
After re-opening/restarting everything, I could do a few confirming commands:
php -i | grep -i scan
Was now properly showing the location to scan for herd's php.ini
file:
Scan this dir for additional .ini files => /Users/peterbabic/Library/Application Support/Herd/config/php/84/
And the openssl.cafile
now had a correct value too:
openssl.cafile => /Users/peterbabic/Library/Application Support/Herd/config/php/cacert.pem => /Users/peterbabic/Library/Application Support/Herd/config/php/cacert.pem
Enjoy!
Links
- https://github.com/beyondcode/herd-community/issues/129#issuecomment-1688116995
- https://github.com/beyondcode/herd-community/issues/267#issuecomment-1850042661
- https://github.com/beyondcode/herd-community/issues/292#issue-2070639721
- https://laracasts.com/discuss/channels/reverb/curl-error-1-received-http09-when-not-allowed