| |

LFCE 55 ๐Ÿง Caddy โ€” Automatic HTTPS

โฑ๏ธ Estimated read: 13 min | |

Caddy’s automatic HTTPS is the feature that distinguishes it from every other web server in this series. Nginx and Apache require the administrator to obtain certificates, write the TLS directives, and set up a renewal mechanism. Caddy performs all of these steps without explicit configuration: a site block containing a public hostname is enough to trigger certificate acquisition, HTTPS serving, HTTP-to-HTTPS redirection, and renewal before expiry . The previous chapter covered the basic Caddyfile and the reverse_proxy directive; this chapter covers the automatic HTTPS behavior itself: the certificate authorities, the ACME challenges, the internal CA for local development, the wildcard certificate requirement, and the on-demand TLS mechanism for custom domains.

Key point: Automatic HTTPS is triggered by the site address. A public hostname like example.com uses Let’s Encrypt or ZeroSSL and requires ports 80 and 443 to be reachable from the internet. A local address like localhost or an internal IP uses Caddy’s internal CA. The tls internal directive forces the internal CA for a site that would otherwise use a public one . Wildcard certificates (*.example.com) require the DNS challenge, which in turn requires a DNS provider plugin.


Why automatic HTTPS is a design decision, not a feature toggle

The multi-issuer fallback problem.

Caddy does not rely on a single certificate authority. It attempts Let’s Encrypt first, then ZeroSSL. If both fail, it backs off exponentially, with a maximum interval of one day between attempts, for up to 30 days. During retries with Let’s Encrypt, it switches to the staging environment to avoid rate limits . This fallback behavior is built into the server, not configured by the administrator. The result is that a transient failure at one CA does not take the site down permanently.

The challenge-selection problem.

ACME requires the server to prove control of the domain. Caddy supports three challenge types: HTTP-01, which serves a token on port 80; TLS-ALPN-01, which uses a special TLS handshake on port 443; and DNS-01, which creates a TXT record and requires a DNS provider plugin . The HTTP and TLS-ALPN challenges are enabled by default, and Caddy randomly chooses between them to avoid accidental dependence on one, then learns which is more successful over time . The DNS challenge is required for wildcard certificates and is configured explicitly.

The internal-CA problem.

A local development site does not have a public DNS record and cannot complete an ACME challenge. Caddy’s internal CA issues certificates from a locally generated root for localhost, 127.0.0.1, and internal IP addresses. The root certificate is installed into the system trust store on first use, or manually with caddy trust if Caddy runs as an unprivileged user . This makes HTTPS possible in development without a public domain or an internet connection to a CA.


a. Public certificates and the default behavior

A site block with a public hostname triggers automatic HTTPS. No tls directive is required.

# /etc/caddy/Caddyfile
example.com {
    root * /var/www/example
    file_server
}

When Caddy starts, it detects the hostname in the site address, obtains a certificate from Let’s Encrypt or ZeroSSL, serves the site over HTTPS on port 443, and redirects HTTP requests on port 80 to the HTTPS URL . The redirect is part of the automatic behavior; it does not require a redir directive or a separate site block.

The ACME account email can be set globally for expiry notifications. The email is optional, but it is recommended because it allows the CA to contact the administrator if there is an issue with the account .

{
    email admin@example.com
}

example.com {
    root * /var/www/example
    file_server
}

The global options block at the top of the Caddyfile applies to all sites. The email option registers the ACME account with the specified address. Caddy may use a different CA for renewal than it used for the initial issuance, so an expiry email from Let’s Encrypt does not necessarily mean the current certificate is from Let’s Encrypt .


b. The internal CA for local development

Sites with local addresses use Caddy’s internal CA instead of a public one. The tls internal directive forces the internal CA for a site that would otherwise qualify for a public certificate.

# Local development with the internal CA
localhost {
    root * /var/www/dev
    file_server
}

# Internal hostname, forced to use the internal CA
app.internal {
    tls internal
    reverse_proxy 127.0.0.1:3000
}

The localhost site uses the internal CA automatically because the address is not a public hostname. The app.internal site uses the .internal TLD, which is excluded from public certificates, and the tls internal directive makes the choice explicit .

The root certificate is stored in Caddy’s data directory, typically $HOME/.local/share/caddy/pki/authorities/local/root.crt. Caddy attempts to install it into the system trust store when it first issues a certificate. If it runs as an unprivileged user or in a container, the installation may fail, and the administrator must run caddy trust or copy the root certificate to the client machines . A client that does not trust the root certificate sees a warning for every site served by the internal CA.


c. Wildcards, DNS challenge, and on-demand TLS

A wildcard certificate covers every subdomain of a domain (*.example.com). Let’s Encrypt requires the DNS-01 challenge for wildcards, which means Caddy must create a TXT record in the domain’s DNS zone. This requires a DNS provider plugin, which is a separate build of Caddy that includes the plugin for the specific provider.

# Global DNS provider configuration
{
    acme_dns cloudflare {env.CLOUDFLARE_API_TOKEN}
}

*.example.com {
    root * /var/www
    file_server
}

The acme_dns global option configures the DNS provider for all sites. The provider plugin must be compiled into the Caddy binary; the standard build does not include it . The wildcard site block then obtains a certificate for *.example.com using the DNS challenge. As of Caddy 2.10, a wildcard certificate can be used for individual subdomains in the configuration without obtaining separate certificates .

On-demand TLS is a different mechanism. It obtains a certificate during the first TLS handshake for a domain, rather than at startup. It is used by SaaS platforms that serve customer-owned domains, where the set of domains is not known in advance. The on_demand_tls global option must include an ask endpoint that Caddy queries to determine whether a certificate should be issued for a domain. Without the ask endpoint, the feature is an abuse vector .

{
    on_demand_tls {
        ask http://localhost:9123/check
    }
}

https:// {
    tls {
        on_demand
    }
    reverse_proxy localhost:8080
}

The ask endpoint receives the domain name and returns HTTP 200 if the certificate should be issued. The application checks the domain against its database of customer domains. The https:// site block with the tls { on_demand } directive handles the certificate acquisition for any domain that passes the check . This is the mechanism that allows a single Caddy instance to serve an unbounded number of customer domains without configuration changes.


Complete Example Session

# ============================================
# PART 1: PUBLIC SITE WITH AUTOMATIC HTTPS
# ============================================
sudo tee /etc/caddy/Caddyfile > /dev/null <<'EOF'
{
    email admin@example.com
}

example.com {
    root * /var/www/example
    file_server
}
EOF

# ============================================
# PART 2: VALIDATE AND RELOAD
# ============================================
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy

# ============================================
# PART 3: VERIFY THE CERTIFICATE
# ============================================
curl -I https://example.com
# Output: HTTP/2 200
# The certificate is issued by Let's Encrypt or ZeroSSL

# ============================================
# PART 4: VERIFY THE HTTP REDIRECT
# ============================================
curl -I http://example.com
# Output: HTTP/1.1 308 Permanent Redirect
#         Location: https://example.com/

# ============================================
# PART 5: LOCAL DEVELOPMENT WITH INTERNAL CA
# ============================================
sudo tee -a /etc/caddy/Caddyfile > /dev/null <<'EOF'

localhost {
    root * /var/www/dev
    file_server
}
EOF

# ============================================
# PART 6: FORCE INTERNAL CA FOR AN INTERNAL HOST
# ============================================
sudo tee -a /etc/caddy/Caddyfile > /dev/null <<'EOF'

app.internal {
    tls internal
    reverse_proxy 127.0.0.1:3000
}
EOF

# ============================================
# PART 7: INSTALL THE INTERNAL ROOT CERTIFICATE
# ============================================
sudo caddy trust
# Installs the root CA into the system trust store

# ============================================
# PART 8: WILDCARD CERTIFICATE WITH DNS CHALLENGE
# ============================================
# Requires a Caddy build with the DNS provider plugin
sudo tee /etc/caddy/Caddyfile > /dev/null <<'EOF'
{
    acme_dns cloudflare {env.CLOUDFLARE_API_TOKEN}
}

*.example.com {
    root * /var/www
    file_server
}
EOF

# ============================================
# PART 9: ON-DEMAND TLS FOR CUSTOM DOMAINS
# ============================================
sudo tee /etc/caddy/Caddyfile > /dev/null <<'EOF'
{
    on_demand_tls {
        ask http://localhost:9123/check
    }
}

https:// {
    tls {
        on_demand
    }
    reverse_proxy localhost:8080
}
EOF

# ============================================
# PART 10: CHECK THE LOGS FOR CERTIFICATE ACTIVITY
# ============================================
sudo journalctl -u caddy -n 30
# Output shows certificate obtained, renewed, or failed

The ten parts demonstrate the automatic HTTPS surface: public certificate issuance, HTTP redirect verification, internal CA for local development, forced internal CA, root certificate installation, wildcard certificate configuration, and on-demand TLS. The critical distinction is between Parts 1 and 5: the public site uses Let’s Encrypt, and the local site uses the internal CA.


Quick Reference

Automatic HTTPS Triggers

Site AddressCertificate SourceRequirements
example.comLet’s Encrypt / ZeroSSLPorts 80 and 443 reachable
localhostInternal CANone
app.internalInternal CAtls internal
*.example.comPublic CADNS challenge + plugin
https:// (catch-all)Public CAon_demand_tls + ask endpoint

ACME Challenges

ChallengePortUse Case
HTTP-0180Default; requires public reachability
TLS-ALPN-01443Default; requires public reachability
DNS-01DNSWildcards; requires provider plugin

tls Directive Options

OptionPurpose
tls internalUse the internal CA
tls { dns <provider> }DNS challenge with provider
tls { on_demand }Obtain certificate on first handshake
tls <email>ACME account email

Verification Commands

CommandPurpose
caddy validate --config /etc/caddy/CaddyfileValidate configuration
caddy trustInstall internal root CA
curl -I https://example.comVerify HTTPS response
curl -I http://example.comVerify HTTP redirect
journalctl -u caddyCheck certificate activity

Best Practices

โœ… Do This:

# Use a public hostname for automatic public certificates
example.com {
    root * /var/www
    file_server
}                                                            # โœ…

# Set the ACME account email globally
{
    email admin@example.com
}                                                            # โœ…

# Use tls internal for local development
localhost {
    tls internal
    file_server
}                                                            # โœ…

# Install the internal root CA on client machines
caddy trust                                                  # โœ…

# Use the DNS challenge for wildcards
{
    acme_dns cloudflare {env.CLOUDFLARE_API_TOKEN}
}                                                            # โœ…

# Use on-demand TLS with an ask endpoint for custom domains
{
    on_demand_tls {
        ask http://localhost:9123/check
    }
}                                                            # โœ…

โŒ Don’t Do This:

# Use a public hostname without DNS pointing to the server
# The ACME challenge fails                                    # โŒ

# Block port 80 at the firewall
# The HTTP challenge and the redirect both require it         # โŒ

# Expect a wildcard certificate without a DNS plugin
# Let's Encrypt requires DNS-01 for wildcards                 # โŒ

# Use on-demand TLS without an ask endpoint
# It is an abuse vector                                       # โŒ

# Forget to install the internal root CA on clients
# Every internal site shows a certificate warning             # โŒ

Common Pitfalls

PitfallWhy It HappensFix
Certificate not issuedDNS does not point to the server, or ports 80/443 blockedVerify DNS and firewall rules
HTTP redirect missingPort 80 blockedOpen port 80
Internal site shows warningRoot CA not trusted on the clientcaddy trust or copy the root certificate
Wildcard certificate failsDNS plugin not included or provider misconfiguredUse a Caddy build with the plugin; check credentials
On-demand TLS abusedask endpoint missing or permissiveAdd the ask endpoint; validate domains
Renewal fails silentlyCA unreachable or challenge failingCheck logs; Caddy backs off exponentially

Real-World Examples

1. Public site with automatic HTTPS

example.com {
    root * /var/www
    file_server
}

2. Local development with internal CA

localhost {
    tls internal
    file_server
}

3. Internal hostname with forced internal CA

app.internal {
    tls internal
    reverse_proxy 127.0.0.1:3000
}

4. Wildcard with DNS challenge

{
    acme_dns cloudflare {env.CLOUDFLARE_API_TOKEN}
}
*.example.com {
    root * /var/www
    file_server
}

5. On-demand TLS

{
    on_demand_tls {
        ask http://localhost:9123/check
    }
}
https:// {
    tls { on_demand }
    reverse_proxy localhost:8080
}

6. Set ACME email

{
    email admin@example.com
}

7. Install internal root CA

caddy trust

8. Verify HTTPS response

curl -I https://example.com

9. Verify HTTP redirect

curl -I http://example.com

10. Check certificate logs

journalctl -u caddy -n 30

Visual

Automatic HTTPS Decision Flow

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  THE SITE ADDRESS DETERMINES THE CERTIFICATE SOURCE          โ”‚
โ”‚                                                              โ”‚
โ”‚  Site address: example.com                                   โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ”œโ”€โ”€ Public hostname? โ†’ YES                             โ”‚
โ”‚       โ”‚     โ””โ”€โ”€ Let's Encrypt โ†’ ZeroSSL โ†’ fallback           โ”‚
โ”‚       โ”‚           โ””โ”€โ”€ HTTP-01 or TLS-ALPN-01 challenge       โ”‚
โ”‚       โ”‚                                                        โ”‚
โ”‚       โ””โ”€โ”€ Wildcard (*.example.com)? โ†’ YES                    โ”‚
โ”‚             โ””โ”€โ”€ DNS-01 challenge required                    โ”‚
โ”‚                   โ””โ”€โ”€ DNS provider plugin required           โ”‚
โ”‚                                                              โ”‚
โ”‚  Site address: localhost or internal IP                      โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ””โ”€โ”€ Public hostname? โ†’ NO                              โ”‚
โ”‚             โ””โ”€โ”€ Internal CA                                  โ”‚
โ”‚                   โ””โ”€โ”€ Root installed with caddy trust        โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

ACME Challenge Types

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  THREE WAYS TO PROVE DOMAIN CONTROL                          โ”‚
โ”‚                                                              โ”‚
โ”‚  HTTP-01:                                                    โ”‚
โ”‚  Client โ†’ Caddy: GET /.well-known/acme-challenge/<token>     โ”‚
โ”‚  Caddy serves the token โ†’ CA verifies โ†’ certificate issued   โ”‚
โ”‚  Requires: port 80 open                                      โ”‚
โ”‚                                                              โ”‚
โ”‚  TLS-ALPN-01:                                                โ”‚
โ”‚  Client โ†’ Caddy: TLS handshake with special ALPN value       โ”‚
โ”‚  Caddy completes the handshake โ†’ CA verifies โ†’ issued        โ”‚
โ”‚  Requires: port 443 open                                     โ”‚
โ”‚                                                              โ”‚
โ”‚  DNS-01:                                                     โ”‚
โ”‚  Caddy creates TXT record via DNS provider plugin            โ”‚
โ”‚  CA queries DNS โ†’ verifies the record โ†’ issued               โ”‚
โ”‚  Requires: DNS plugin, no open ports                         โ”‚
โ”‚  Required for: wildcard certificates                         โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Internal CA Trust Flow

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  THE ROOT CERTIFICATE MUST BE TRUSTED BY THE CLIENT          โ”‚
โ”‚                                                              โ”‚
โ”‚  Caddy host:                                                 โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
โ”‚  โ”‚ caddy trust                                            โ”‚  โ”‚
โ”‚  โ”‚ โ†’ installs root.crt into the system trust store        โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ”‚                                                              โ”‚
โ”‚  Other clients:                                              โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
โ”‚  โ”‚ Copy root.crt from the Caddy host:                     โ”‚  โ”‚
โ”‚  โ”‚ $HOME/.local/share/caddy/pki/authorities/local/root.crtโ”‚  โ”‚
โ”‚  โ”‚ โ†’ install into the client's trust store                โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ”‚                                                              โ”‚
โ”‚  Without trust: certificate warning on every internal site.  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Summary

ItemValue
TriggerSite address with a public hostname
Default CAsLet’s Encrypt, then ZeroSSL
Fallback behaviorExponential backoff, staging on retry
Default challengesHTTP-01 and TLS-ALPN-01
Wildcard challengeDNS-01 (requires plugin)
Local developmentInternal CA
Force internal CAtls internal
Internal root location$HOME/.local/share/caddy/pki/authorities/local/root.crt
Root installationcaddy trust
On-demand TLSon_demand_tls + ask endpoint
HTTP redirectAutomatic when a hostname is configured

Key takeaways:

  • Automatic HTTPS is triggered by the site address. A public hostname obtains a certificate from Let’s Encrypt or ZeroSSL, serves HTTPS, and redirects HTTP. No tls directive is required .
  • The default challenges are HTTP-01 and TLS-ALPN-01. They require ports 80 and 443 to be reachable from the internet. Caddy randomly chooses between them and learns which is more successful .
  • Wildcard certificates require the DNS-01 challenge. The DNS challenge requires a DNS provider plugin, which is a separate Caddy build. The acme_dns global option or the tls { dns ... } directive configures it .
  • The internal CA serves local addresses. localhost, internal IPs, and the .internal TLD use the internal CA automatically. The tls internal directive forces it for a site that would otherwise qualify for a public certificate .
  • The internal root must be trusted by clients. caddy trust installs it on the Caddy host. Other clients need the root certificate copied and installed into their trust stores .
  • On-demand TLS is for customer-owned domains. It obtains a certificate during the first handshake. The ask endpoint is mandatory to prevent abuse, and the application must validate the domain before returning HTTP 200 .

Remember: Caddy’s automatic HTTPS is not a configuration option; it is the default behavior for any site with a public hostname. The certificate is obtained, the redirect is configured, and the renewal is scheduled without a single TLS directive. The internal CA covers local development, the DNS challenge covers wildcards, and on-demand TLS covers custom domains. The administrator’s job is to point the DNS, open the ports, and choose the certificate source when the default is not appropriate. The rest is automatic.


Was this helpful?


Stop using slow, ad-bloated tool sites! ๐Ÿคฎ

๐Ÿ”Ž Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
โœ… Finance (Mortgage, Interest, Inflation)
โœ… Tech (Base64, JSON, Dev Suite, IP)
โœ… Health (BMI, BMR, TDEE)
โœ… Productivity (Timer, Workspace, QR)

โšก๏ธ Fast & Private
๐Ÿ”’ No data leaves your device
๐Ÿ’Ž 100% Free

๐Ÿ”— Use it now: https://tools.kandz.me
๐Ÿ”– Bookmark itโ€”youโ€™ll need it later!