Skip to content
Move custom links under /@handle
Sniptide

Caddyfile reverse proxy

This Caddyfile puts your app on example.com with an automatic HTTPS certificate, compression, security headers and a www redirect. Replace example.com with your domain and localhost:3000 with your app's address, point the domain's DNS at the server, and reload Caddy.

Updated

Files

CaddyfileRaw
{
	# Let's Encrypt sends certificate expiry notices here.
	email [email protected]
}

example.com {
	encode zstd gzip

	reverse_proxy localhost:3000

	header {
		Strict-Transport-Security "max-age=31536000; includeSubDomains"
		X-Content-Type-Options nosniff
		Referrer-Policy strict-origin-when-cross-origin
		-Server
	}

	log {
		output file /var/log/caddy/example.com.log
	}
}

www.example.com {
	redir https://example.com{uri} permanent
}

Save with your agent

Connect the Sniptide MCP server once, then ask your agent to keep this template in your library. It reads the files from the JSON version of this page and saves them as a private snippet you can edit and file into a collection.

Terminal
claude mcp add --transport http sniptide https://app.sniptide.com/api/mcp
Prompt
Read https://sniptide.com/templates/caddyfile-reverse-proxy.json and save its files as a private Sniptide snippet titled "Caddyfile reverse proxy".

Run it

Caddy reads /etc/caddy/Caddyfile when it runs as a system service. Check the file, then reload without dropping connections:

Terminal
caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy

Ports 80 and 443 must be open on the server and the domain's A or AAAA record must point at it. Caddy then gets a certificate from Let's Encrypt on its own and renews it before it expires.

What each part does

Part
What it does
When to change it
Global options
The block at the top without a site name. email is the address for certificate notices.
Add options such as admin off here.
example.com
A site block. A domain name as the address turns on HTTPS and the redirect from HTTP.
Use your domain. List several, separated by commas, to serve them the same way.
encode
Compresses responses with zstd or gzip, whichever the browser accepts.
Rarely.
reverse_proxy
Sends every request to your app. Caddy sets X-Forwarded-For and X-Forwarded-Proto for it.
Point it at your app's address and port.
header
Adds security headers. -Server removes Caddy's own Server header.
Add a Content-Security-Policy once you know which sources your app loads.
log
Writes an access log for the site. Without it, Caddy logs no requests.
Remove it, or log to stdout in a container.
www.example.com
Redirects the www name to the bare domain, keeping the path.
Swap the two if you prefer www.

Only add Strict-Transport-Security once HTTPS works for the domain and every subdomain, because browsers then refuse plain HTTP for a year.

Variant: Caddy in Docker Compose

In Compose, the app is reached by its service name. The caddy_data volume keeps certificates across restarts, which matters because Let's Encrypt limits how often you can request new ones.

compose.yaml
services:
  caddy:
    image: caddy:2
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data
      - caddy_config:/config

  app:
    build: .

volumes:
  caddy_data:
  caddy_config:
Caddyfile
example.com {
	reverse_proxy app:3000
}

Variant: an API and a front end on one domain

handle_path strips the matched prefix before proxying, so the API sees /users instead of /api/users. Use handle to keep the prefix.

Caddyfile
example.com {
	handle_path /api/* {
		reverse_proxy localhost:4000
	}

	handle {
		reverse_proxy localhost:3000
	}
}

Variant: several app servers

List more than one upstream and Caddy balances between them, skipping any that fail their health check.

Caddyfile
example.com {
	reverse_proxy app1:3000 app2:3000 {
		lb_policy round_robin
		health_uri /api/health
		health_interval 10s
	}
}

To run Sniptide on your own server behind Caddy, follow the reverse proxy guide.

All templates
  • Docker and deployNext.js Dockerfile

    A multi-stage Next.js Dockerfile that builds the standalone output into a small image running as a non-root user, with .dockerignore and variants.

  • Docker and deployDocker Compose Postgres

    A Docker Compose file for Postgres with a persistent volume, a health check and init scripts, plus variants with an app service and pgAdmin.

  • .env
    Project setup.env.example template

    A .env.example template with grouped variables, safe defaults and empty secrets, plus how to keep it in sync with your code and share the real values.

Pick where your library lives.

Use sniptide.com

Save your first pattern in the app, then connect Claude Code, Cursor or Codex with one sign-in in the browser.

Run it on your own server

One image and a database give you the app, the API and the MCP endpoint. Sniptide is open source under AGPL-3.0.

$docker compose up -d