Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Lets Encrypt Laravel Package

hyn/lets-encrypt

A Laravel package for automating Let's Encrypt SSL certificates. It provides Artisan commands and a straightforward API to request, validate, and renew certificates, helping keep your web applications securely served over HTTPS with minimal manual effort.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install via Composer (this is a Laravel-era package, so pin it appropriately for your framework version):

    composer require hyn/lets-encrypt
    
  2. Publish the config:

    php artisan vendor:publish --provider="Hyn\LetsEncrypt\LetsEncryptServiceProvider"
    

    This gives you config/letsencrypt.php, which is where almost all the behavior lives.

  3. Look at the config first. The three values that matter on day one:

    • email — required for ACME account registration with Let's Encrypt.
    • domains — the FQDN(s) you're requesting a certificate for.
    • certificate_path / key_path (or whatever the package version names them) — where the issued cert and private key are written.
  4. Ensure prerequisites are met before running anything:

    • The domain's DNS A record already points at the box.
    • Port 80 is reachable from the public internet (HTTP-01 challenge).
    • Your web server is configured to serve /.well-known/acme-challenge/ out of the package's challenge directory.
  5. First use case — issue a certificate:

    php artisan letsencrypt:generate
    

    The interactive flow walks you through registering an ACME account with your email and requesting the cert. On success you'll have a cert/key pair at the configured paths that you point nginx/Apache at.

  6. Verify with openssl x509 -in <certificate_path> -noout -text and reload your web server.

Implementation Patterns

Scheduled renewal (the intended workflow). Certificates from Let's Encrypt last 90 days; the package is designed to be run from the scheduler. In app/Console/Kernel.php:

protected function schedule(Schedule $schedule)
{
    $schedule->command('letsencrypt:renew')
        ->daily()
        ->withoutOverlapping();
}

You must have php artisan schedule:run in cron every minute for this to fire — the package does not install its own daemon.

Reload the web server after renewal. The package writes files; it does not know how to reload nginx. Chain the reload in a wrapper command or a scheduled job:

$schedule->command('letsencrypt:renew')
    ->daily()
    ->withoutOverlapping()
    ->then(fn () => exec('sudo systemctl reload nginx'));

Multi-domain certs. Put all SANs in domains in the config and issue once. All names must resolve to the same host for HTTP-01 validation to pass from a single box.

Stage vs. production. Use the ACME staging/directory endpoint in config/letsencrypt.php during development. Hitting production with a broken challenge setup eats your rate limit for hours. Point the staging endpoint at https://acme-staging-v02.api.letsencrypt.org/directory (or the v1 equivalent in older versions) while you get the .well-known route working.

Integration with deploy pipelines. Treat issuance as an infra step, not a per-request thing:

  • First deploy: run letsencrypt:generate.
  • Subsequent deploys: ensure the cert/key paths are on persistent storage (not inside a release/ folder that gets swapped out).
  • If you use symlink releases (Envoyer/Deployer), point certificate_path and key_path at a shared directory outside the release.

Environment-config driven. Keep the email and domains in .env/config rather than hardcoding, especially if the same app is deployed to multiple environments:

'email'   => env('LETSENCRYPT_EMAIL'),
'domains' => explode(',', env('LETSENCRYPT_DOMAINS', '')),

Challenge serving. A minimal nginx snippet for the validation route:

location /.well-known/acme-challenge/ {
    root /path/to/storage/app/letsencrypt;
    try_files $uri =404;
}

Point the root at whatever directory the package drops token files into (check the config for storage_path / challenge path).

Gotchas and Tips

This package is archived — plan the exit ramp. It is not maintained, and Let's Encrypt's ACME v1 endpoints were retired in 2021. If the version you have is hardcoded to v1, issuance will fail against production. Before investing, check composer.lock / the source for the ACME directory URL. If it's v1, fork it, patch the directory to the v2 endpoint, or migrate to something actively maintained (acmephp/acmephp or certbot invoked from a shell command).

Port 80 is not optional for HTTP-01. If you want a cert on a box where only 443 is open, this package's default HTTP-01 flow won't work. You'll need DNS-01 (not supported by this package in most versions) or a proxy that forwards .well-known from port 80.

"Reused existing account" but renewal fails — usually the account key file is missing or unreadable. Check storage/ permissions. The web user (php-fpm) and the CLI user (running artisan) often differ, so key files created by one and read by the other fail silently.

Permission trap on private keys. The package writes the private key with the umask of whoever ran the command. If root ran letsencrypt:generate, the key may be 0600 root and unreadable by nginx's www-data. Either run as the deploy user and chown, or chmod in a post-step. Never chmod 644 a key file.

Debugging validation failures. Run with -v/-vvv. The most common silent failure is that the ACME server fetched http://yourdomain/.well-known/acme-challenge/<token> and got a 404 or a 301 redirect to HTTPS that breaks the token. Confirm with curl -v from an external host, not localhost.

Rate limits are brutal and real. Let's Encrypt allows ~5 duplicate certificates per domain per week and ~5 failures per account per hostname per hour. During debugging, use staging (unlimited) until the challenge route is proven, then flip to production. If you've already been rate-limited, check https://crt.sh/?q=yourdomain to see how many certs were actually issued.

The renew command is not idempotent by name but is by effect — it checks expiry and only reissues if the cert is near the threshold. Don't assume running it twice creates two certs; but do be careful if the check is based on file mtime and your deploy process touches files.

Scheduler overlapping. Without ->withoutOverlapping(), a slow ACME round-trip plus a daily cron can stack renewals and burn rate limits. Add it.

Wildcards are not supported. Older ACME wrappers predate *.domain.com support. If you need a wildcard, this package won't do it — you need DNS-01 and a modern client.

Config caching will bite you. If you ran php artisan config:cache, changes to config/letsencrypt.php are ignored until you config:clear. The same trap exists for the .env values it reads.

Extension points are thin. The package is a fairly rigid wrapper — no driver abstraction, no events fired around issuance. If you need to hook in (e.g., log to your monitoring, notify Slack on renew), wrap the artisan command in a custom command that shells out and handles the result rather than trying to subclass the package.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings