1 of 34

Better PHP Sites with the Caddy Web Server

How API Platform Leverages Caddy to Power Your PHP Apps

by Matt Holt API Platform Conference September 10, 2021 Lille, France

2 of 34

Typical PHP application architecture using PHP-FPM

Web server

Caddy or nginx

FastCGI responder

php-fpm

Client

Web browser, app, curl…

TLS +

HTTP/1.1, HTTP/2, HTTP/3, WebSockets, etc.

FastCGI

PHP worker

PHP worker

PHP worker

PHP

PHP

PHP

.php files

Page resources

CSS, JS, images...

Static files

Disk I/O

3 of 34

Some things web servers do for your PHP site

  • Terminate TLS
  • Decode HTTP
  • Route requests (static files versus proxy to app)
  • Talk FastCGI to php-fpm
  • Reject malicious or mischievous requests
  • Headers (CORS, etc)
  • Cache
  • Compress
  • Log
  • Redirect
  • Rewrite
  • Reverse proxy / load balancing …
  • Static file serving ...

Web server

Caddy or nginx

TLS +

HTTP/1.1, HTTP/2, HTTP/3, WebSockets, etc.

FastCGI

Disk I/O

4 of 34

C A D D Y

5 of 34

Overview

Caddy is a highly extensible, self-hosted platform on which you can build, configure, and deploy long-running services ("apps").

  • https://caddyserver.com (/v2)
  • 100% open source web server, Apache-licensed
  • Called "Caddy" because it helps you focus on being productive
  • "The Ultimate Server"
  • Fewer moving parts
  • Hundreds of contributors
  • Millions of downloads
  • Trillions of connections secured
  • Only server to use TLS automatically and by default

6 of 34

Automatic HTTPS

  • HTTPS by default
  • Obtains and renews TLS certificates
  • Gold standard implementation
  • Scales vertically: tens of thousands of sites (or more)
  • Scales horizontally: coordinates and shares in a cluster
  • PKI facilities to operate its own internal CA

More on this later!

7 of 34

Memory safety

  • 80% of exploited vulnerabilities are memory safety issues, including:
    • Out-of-bounds reads and writes
    • Use-after-free
  • Written in Go
  • More memory-safe than C programs
  • Exploits like Heartbleed are virtually impossible

8 of 34

Static binary

  • No runtime library dependencies (not even libc)
  • Easy to compile
  • Easy to deploy
  • Hard to break production by accident
  • Strongly-versioned modules
  • No container needed

9 of 34

Extensible

  • Dozens of third-party plugins
    • Vulcain and Mercure
    • DNS control
    • Service integrations
    • Rate limiting
    • Authentication and authorization
    • Other protocols: PROXY, WebDAV, etc.
    • CGI
    • TCP + UDP proxying
    • Process supervisor
    • Geolocation
    • And more…
  • Easy to write plugins (Go)
  • Automatic documentation

10 of 34

Online Config API

  • JSON config
    • Exportable
    • Traversable
  • REST endpoint
  • ACID guarantees:
    • Atomic
    • Consistent
    • Isolated
    • Durable
  • Zero-downtime changes
  • Can resume last working config after restart

$ curl "http://localhost:2019/config/" | jq

{

"apps": {

"http": {

"servers": {

"myserver": {

"listen": [":443"],

"routes": [

{

"match": [

{"host": ["example.com"]}

],

"handle": [

{"handler": "file_server"}

]

}

]

}

}

}

}

}

$ curl "http://localhost:2019/config/apps/http/servers/myserver/listen"

[":443"]

11 of 34

Config adapters

  • Plugins that convert any input to JSON
  • Config your server your way
  • Known existing adapters:
    • Caddyfile
    • JSON-C / JSON 5
    • YAML
    • TOML
    • CUE
    • HCL
    • NGINX (!)

$ caddy run --config nginx.conf --adapter nginx

12 of 34

Caddyfile

  • Most popular way to configure Caddy
  • Easy to write by hand
  • Easy to read
  • Not 100% parity with JSON, but sufficient for most users

More on this later!

13 of 34

Native PHP + FastCGI Support

  • FastCGI is a reverse proxy transport module
  • Easy to configure
  • One-liner "just works" for most PHP apps

php_fastcgi 127.0.0.1:9000

14 of 34

php_fastcgi php-fpm:9000

route {

# Add trailing slash for directory requests

@canonicalPath {

file {path}/index.php

not path */

}

redir @canonicalPath {path}/ 308

# If the requested file does not exist, try index files

@indexFiles file {

try_files {path} {path}/index.php index.php

split_path .php

}

rewrite @indexFiles {http.matchers.file.relative}

# Proxy PHP files to the FastCGI responder

@phpFiles path *.php

reverse_proxy @phpFiles php-fpm:9000 {

transport fastcgi {

split .php

}

}

}

15 of 34

Traefik → Caddy

16 of 34

Typical Caddyfile config for PHP sites

example.com

root * /var/www/html

php_fastcgi php-fpm:9000

file_server

example.com

root * /var/www/html

php_fastcgi php-fpm:9000 php-fpm-2:9000

file_server

17 of 34

H T T P S

18 of 34

Automatic HTTPS

19 of 34

How Automatic HTTPS Works

Provisions certificates when config is loaded.

  1. Caddy extracts domain names from config
  2. Looks for existing certificate in storage or memory
  3. Solves ACME challenge
  4. Saves certificate in storage
  5. Loads certificate into memory

20 of 34

The 3 ACME Challenges

HTTP

:80

TLS-ALPN

:443

DNS

1

2

3

ACME server (CA)

DNS server

Your server (Caddy)

21 of 34

✅ Rate limiting

✅ Failed validations

✅ Revocations

✅ Infrastructure outages

✅ Customer domains

Production Challenges of TLS

✅ OCSP problems

✅ Misconfigured storage

✅ Fleet coordination

✅ Dynamic updates

✅ Scaling millions of certs

= Caddy handles it�(other scripts/tools… don't usually)

22 of 34

O N - D E M A N D T L S

23 of 34

On-Demand TLS

Provisions certificates "on-demand" at run-time.

  • Obtains cert at first TLS handshake that requires it
  • Renews certificate in background if used ("passive maintenance")
  • Perfect when you don't control the domain names
  • Scales to hundreds of thousands of certs
  • Works in a cluster

24 of 34

On-Demand TLS Example

{

on_demand_tls {

ask http://localhost:5555/check

}

}

https://

tls {

on_demand

}

...

(The "ask" endpoint might be a PHP script that checks your database to see if the domain name is associated with a customer.)

25 of 34

A P I P L A T F O R M

26 of 34

"Caddy allowed us to dramatically simplify the setup we provide (both in dev and in prod). Its main strengths are batteries included and very easy to extend, and we need that for API Platform."

-Kévin Dunglas, creator of API Platform

27 of 34

API Platform Architecture Comparison

API Platform switched to Caddy in v2.6; see PR 1693

Ref: https://medium.com/@dunglas/api-platform-2-6-8bd3a506a9c4

NGINX

Caddy

8 containers:

  1. PHP-FPM
  2. NGINX
  3. A container to generate TLS certificates
  4. Vulcain reverse proxy
  5. Mercure Hub
  6. Postgres
  7. A Node.js container for the client generator
  8. A Node.js container for the admin app

4 containers:

  1. PHP-FPM
  2. Caddy web server
  3. Postgres (optional)
  4. Next.js container (optional)

❌ No memory safety ( Heartbleed, etc; because C)

✅ Memory-safe (Go)

❌ Manual HTTPS

✅ Automatic HTTPS that scales

❌ Difficult to extend

✅ Easily extensible

❌ Usually has external dependencies

✅ Static binary, no dependencies

28 of 34

API Platform's Caddyfile

{

{$DEBUG}

servers {

protocol {

experimental_http3

}

}

}

{$SERVER_NAME}

log

@pwa expression `(

{header.Accept}.matches("\\btext/html\\b")

&& !{path}.matches("(?i)(?:^/docs|^/graphql|^/bundles/|^/_profiler|^/_wdt|\\.(?:json|html$|csv$|ya?ml$|xml$))")

)

|| {path} == "/favicon.ico"

|| {path} == "/manifest.json"

|| {path} == "/robots.txt"

|| {path}.startsWith("/_next")

|| {path}.startsWith("/sitemap")`

route {

root * /srv/api/public

mercure {

transport_url {$MERCURE_TRANSPORT_URL:bolt:///data/mercure.db}

publisher_jwt {env.MERCURE_PUBLISHER_JWT_KEY} {env.MERCURE_PUBLISHER_JWT_ALG}

subscriber_jwt {env.MERCURE_SUBSCRIBER_JWT_KEY} {env.MERCURE_SUBSCRIBER_JWT_ALG}

anonymous

subscriptions

{$MERCURE_EXTRA_DIRECTIVES}

}

vulcain

push

header ?Link `</docs.jsonld>; rel="http://www.w3.org/ns/hydra/core#apiDocumentation", </.well-known/mercure>; rel="mercure"`

header ?Permissions-Policy "interest-cohort=()"

reverse_proxy @pwa http://{$PWA_UPSTREAM}

php_fastcgi unix//var/run/php/php-fpm.sock

encode zstd gzip

file_server

}

29 of 34

T H E P O I N T

30 of 34

Knowing Caddy gives you technical and competitive advantages for your sites

31 of 34

32 of 34

75%

of surveyed consumers would feel more likely to recommend or purchase from a company that sponsors open source

According to a scientifically rigorous* Twitter survey:

* not really

33 of 34

Support open source development

Corporations rely on open source, and open source relies on corporations.

  • I work on Caddy full-time
  • Caddy's executive sponsor: ZeroSSL
  • Sponsorships recommended for:
    • Companies that use the project
    • Companies whose customers use the projects
    • Professionals who rely on the project
  • Sponsoring is good for your business:
    • 75% of respondents would be more likely to recommend or buy from open source sponsors
    • Customers feel more loyal toward companies that sponsor open source

34 of 34

Thank you