FrankenPHP HTTPS on an IP Address via Let's Encrypt | Mohamed Said        [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://msaied.com) [ Home ](https://msaied.com) [ Projects ](https://msaied.com/projects) [ Articles  ](https://msaied.com/articles) [ Certificates ](https://msaied.com/certificates) [ Contact ](https://msaied.com#contact-section) 

       [  ](https://github.com/EG-Mohamed)       

 [ Home ](https://msaied.com) [ Projects ](https://msaied.com/projects) [ Articles ](https://msaied.com/articles) [ Certificates ](https://msaied.com/certificates) [ Contact ](https://msaied.com#contact-section) 

  [ home ](https://msaied.com)    [ articles ](https://msaied.com/articles)    Let's Encrypt HTTPS on an IP Address With FrankenPHP        On this page       1. [  HTTPS for IP Addresses, No DNS Required ](#https-for-ip-addresses-no-dns-required)
2. [  How the Short-Lived Certificate Profile Works ](#how-the-short-lived-certificate-profile-works)
3. [  Configuring It in Docker Compose ](#configuring-it-in-docker-compose)
4. [  IPv6 Note ](#ipv6-note)
5. [  Other Changes in v4.6.0-beta1 ](#other-changes-in-v460-beta1)
6. [  Key Takeaways ](#key-takeaways)

  ![Let's Encrypt HTTPS on an IP Address With FrankenPHP](https://cdn.msaied.com/567/98b933f95337b9a58889257f2e3223b4.png)

 [  Laravel ](https://msaied.com/articles?category=laravel) [  PHP ](https://msaied.com/articles?category=php)  #FrankenPHP   #Docker   #HTTPS   #Let's Encrypt   #Caddy   #PHP  

 Let's Encrypt HTTPS on an IP Address With FrankenPHP 
======================================================

     17 Aug 2026      4 min read    ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said  

       Table of contents

1. [  01   HTTPS for IP Addresses, No DNS Required  ](#https-for-ip-addresses-no-dns-required)
2. [  02   How the Short-Lived Certificate Profile Works  ](#how-the-short-lived-certificate-profile-works)
3. [  03   Configuring It in Docker Compose  ](#configuring-it-in-docker-compose)
4. [  04   IPv6 Note  ](#ipv6-note)
5. [  05   Other Changes in v4.6.0-beta1  ](#other-changes-in-v460-beta1)
6. [  06   Key Takeaways  ](#key-takeaways)

 HTTPS for IP Addresses, No DNS Required
---------------------------------------

The [`serversideup/docker-php`](https://github.com/serversideup/docker-php) images shipped `v4.6.0-beta1` with a new `CADDY_ACME_PROFILE` environment variable. Set it to `shortlived` and a FrankenPHP container reachable only by IP address can obtain a real, browser-trusted TLS certificate from Let's Encrypt — no domain name, no DNS record, no self-signed warning.

Let's Encrypt made 6-day certificates and IP-address certificates generally available in January 2026. The `shortlived` profile is the only one under which Let's Encrypt issues IP certificates, so the two features arrive together.

How the Short-Lived Certificate Profile Works
---------------------------------------------

Setting `CADDY_ACME_PROFILE=shortlived` writes the following snippet into the Caddy configuration:

```
cert_issuer acme {
  profile shortlived
}

```

Caddy then requests certificates valid for **160 hours** (just under seven days), renewing them every two days. That lifetime qualifies as a Short-Lived Subscriber Certificate under CA/Browser Forum rules, which means revocation information is less critical — a compromised key becomes useless in days rather than months.

A few operational trade-offs to keep in mind:

- Using any explicit profile drops the ZeroSSL fallback the images normally keep.
- If the container loses outbound access to Let's Encrypt's ACME API, you have roughly **four and a half days** before the certificate expires.
- Let's Encrypt allows five certificates per identical identifier set every seven days, so repeated redeploys without persistent volumes can exhaust that quota quickly.

Configuring It in Docker Compose
--------------------------------

The feature is only in the beta tags. Pin to `v4.6.0-beta1` rather than the stable `8.4-frankenphp` tag:

```yaml
services:
  php:
    image: serversideup/php:8.4-frankenphp-v4.6.0-beta1
    ports:
      - "80:8080"
      - "443:8443"
    volumes:
      - caddy-config:/config
      - caddy-data:/data
    environment:
      SSL_MODE: "full"
      CADDY_AUTO_HTTPS: "on"
      CADDY_ACME_PROFILE: "shortlived"
      CADDY_HTTPS_SERVER_ADDRESS: "https://203.0.113.10"
      CADDY_GLOBAL_OPTIONS: "default_sni example.com"

volumes:
  caddy-config:
  caddy-data:

```

**Key variables explained:**

- `CADDY_AUTO_HTTPS: "on"` — enables Caddy's automatic HTTPS (off by default).
- `SSL_MODE: "full"` — serves the app over HTTPS and redirects HTTP with a 308.
- `CADDY_HTTPS_SERVER_ADDRESS` — the public IP address Caddy should obtain a certificate for.
- `CADDY_GLOBAL_OPTIONS: "default_sni example.com"` — provides a fallback SNI identity, because a client connecting to a bare IP sends no Server Name Indication field.

Both ports 80 and 443 (mapped from 8080/8443 inside the unprivileged container) must be reachable from the internet for the ACME challenge to complete.

**Mount `/config` and `/data` as named volumes.** Certificates and ACME account state live there. A container that discards them on every restart will request fresh certificates on every restart, burning through the rate limit fast.

### IPv6 Note

Caddy fixed IPv6 handling for IP certificates in issue #7399, closed in April 2026. Caddy v2.11.4 (embedded in the current images) includes that fix, but IPv4 has a longer track record — test IPv6 thoroughly before relying on it in production.

Other Changes in v4.6.0-beta1
-----------------------------

- **`TRUSTED_PROXY`** now behaves consistently across FrankenPHP, NGINX, and Apache, so `request()->ip()` returns the visitor IP regardless of which server variant is running.
- A new **`caddyfile-global.d`** directory lets you drop custom global Caddy config snippets.
- **`healthcheck-nightwatch`** integrates Laravel Nightwatch status checks with Docker `HEALTHCHECK`.
- **`AUTORUN_LARAVEL_SKIP_IF_NOT_FOUND`** allows a container with `AUTORUN_ENABLED=true` to exit cleanly when Laravel is not yet installed.

Key Takeaways
-------------

- `CADDY_ACME_PROFILE=shortlived` is the single variable that unlocks both short-lived and IP-address certificates.
- No DNS is required — ideal for staging boxes, internal APIs, VPN-only servers, and demo droplets.
- Certificates last 160 hours and renew every two days; persistent volumes are essential to avoid rate-limit exhaustion.
- The feature is in a **prerelease** tag; do not use it in production environments you cannot afford to break.
- ZeroSSL fallback is disabled when any explicit ACME profile is set.

---

*Source: [Let's Encrypt HTTPS on an IP Address With FrankenPHP — Laravel News](https://laravel-news.com/frankenphp-https-ip-address)*

 Found this useful?

          [  ](https://twitter.com/intent/tweet?url=https%3A%2F%2Fmsaied.com%2Farticles%2Flets-encrypt-https-on-an-ip-address-with-frankenphp&text=Let%27s+Encrypt+HTTPS+on+an+IP+Address+With+FrankenPHP) [  ](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fmsaied.com%2Farticles%2Flets-encrypt-https-on-an-ip-address-with-frankenphp) 

 Frequently Asked Questions 
----------------------------

  3 questions  

     Q01  Can Let's Encrypt issue a TLS certificate for a bare IP address instead of a domain name?        Yes, as of January 2026 Let's Encrypt issues IP-address certificates, but only under the `shortlived` certificate profile. That profile produces certificates valid for 160 hours (just under seven days). The `serversideup/php` v4.6.0-beta1 images expose this through the `CADDY_ACME_PROFILE=shortlived` environment variable. 

      Q02  Why must /config and /data be mounted as persistent volumes when using IP certificates with FrankenPHP?        Caddy stores the issued certificate and the ACME account state in those directories. If they are lost on container restart, Caddy requests new certificates on every restart. Let's Encrypt allows only five certificates per identical identifier set every seven days, so a few redeploys without persistent volumes can exhaust the weekly quota. 

      Q03  What does CADDY\_GLOBAL\_OPTIONS: "default\_sni example.com" do in this setup?        When a client connects to a bare IP address it sends no Server Name Indication (SNI) field, because there is no hostname to advertise. Without a fallback, Caddy cannot match a certificate to the connection. Setting `default_sni` gives Caddy a fallback identity to use for those SNI-less connections. 

  Continue reading

 More Articles 
---------------

 [ View all    ](https://msaied.com/articles) 

 [ ![Object Storage Migrations with Laravel's Read-Through Filesystem](https://cdn.msaied.com/565/f830d15d4a1287d381fa05e631ea2aba.png) Laravel 13 Object Storage S3 

### Object Storage Migrations with Laravel's Read-Through Filesystem

Laravel 13 introduces a read-through filesystem driver that lets you migrate from S3 to R2 without downtime. N...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 18 Aug 2026     4 min read  

  Read    

 ](https://msaied.com/articles/object-storage-migrations-with-laravels-read-through-filesystem) [ ![Livewire v4.4.1 Released: Bug Fixes, Alpine 3.16.2, and Laravel 13 Compatibility](https://cdn.msaied.com/564/c64b65959ad8ade491c78f3482f22996.png) Livewire Laravel Alpine.js 

### Livewire v4.4.1 Released: Bug Fixes, Alpine 3.16.2, and Laravel 13 Compatibility

Livewire v4.4.1 ships 16 fixes and improvements including Alpine.js bumped to 3.16.2, cached computed property...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 18 Aug 2026     3 min read  

  Read    

 ](https://msaied.com/articles/livewire-v441-released-bug-fixes-alpine-3162-and-laravel-13-compatibility) [ ![MySQL EXPLAIN and Query Profiling in Laravel: Finding Slow Queries Before They Hit Production](https://cdn.msaied.com/563/f2d4a7fb0ab45706cf9330746f7b2588.png) laravel mysql performance 

### MySQL EXPLAIN and Query Profiling in Laravel: Finding Slow Queries Before They Hit Production

Learn how to read MySQL EXPLAIN output, use query profiling tools, and integrate them into a Laravel workflow...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 18 Aug 2026     3 min read  

  Read    

 ](https://msaied.com/articles/mysql-explain-and-query-profiling-in-laravel-finding-slow-queries-before-they-hit-production) 

   [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://msaied.com)Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

Explore

- [Home](https://msaied.com)
- [Projects](https://msaied.com/projects)
- [Articles](https://msaied.com/articles)
- [Certificates](https://msaied.com/certificates)
- [Contact](https://msaied.com#contact-section)

Connect

- [   hello@msaied.com ](mailto:hello@msaied.com)
- [   +20 109 461 9204 ](tel:+201094619204)

© 2026 Mohamed Said. All rights reserved.

 [  ](https://github.com/EG-Mohamed) [  ](https://www.linkedin.com/in/msaiedm/) [  ](https://wa.me/201094619204) [  ](mailto:hello@msaied.com) [  ](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)
