Translating .htaccess RewriteRule to Caddyfile

1. The problem I’m having:

I am moving a basic PHP site from Apache to Caddy. I had the following .htaccess file in a directory:

RewriteEngine on
RewriteRule ^example1\.json$ /app/v1/example1/index.php [NC,L]
RewriteRule ^example2\.json$ /app/v1/example2/index.php [NC,L]

I cannot figure out how this can be translated into a Caddyfile.

If I understand the rewrite correctly, it is basically rewriting these 2 .json files to separate index.php files so that end users can point to the .json but get the output of the .php files. There’s probably a better way of doing this, but this is the PHP application I’m working with currently.

2. Error messages and/or full log output:

No error message because Caddy is working. I just don’t know the correct incantation to get the rewrite to work in Caddy.

3. Caddy version:

v2.6.4 h1:2hwYqiRwk1tf3VruhMpLcYTg+11fCdr8S3jhNAdnPy8=

With the Cloudflare DNS module.

4. How I installed and ran Caddy:

I used the following Dockerfile to create the Caddy image:

FROM caddy:2.6.4-builder-alpine AS build

RUN XCADDY_SKIP_CLEANUP=0 xcaddy build --with github.com/caddy-dns/cloudflare

FROM caddy:2.6.4-alpine AS final

COPY --from=build /usr/bin/caddy /usr/bin/caddy

a. System environment:

  • Linode with Fedora 37 image—all software and security updates installed
  • AMD EPYC 7713 64-Core Processor
  • Podman: v4.4.2 w/ Go v1.19.6 for OS/Arch linux/amd64

b. Command:

I use Github actions to automatically build and tag my Docker images. This is the Github Action to build the image:

name: Docker Image

on:
  push:
    branches:
      - main
      - seed

    tags:
      - v*

  pull_request:

env:
  IMAGE_NAME: caddy-cf

jobs:
  push:
    runs-on: ubuntu-latest
    permissions:
      packages: write
      contents: read

    steps:
      - uses: actions/checkout@v3

      - name: Build image
        run: docker build . --file Dockerfile --tag $IMAGE_NAME --label "runnumber=${GITHUB_RUN_ID}"

      - name: Log in to registry
        run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u $ --password-stdin

      - name: Push image
        run: |
          IMAGE_ID=ghcr.io/${{ github.repository_owner }}/$IMAGE_NAME

          IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]')
          VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,')
          [[ "${{ github.ref }}" == "refs/tags/"* ]] && VERSION=$(echo $VERSION | sed -e 's/^v//')
          [ "$VERSION" == "main" ] && VERSION=latest
          echo IMAGE_ID=$IMAGE_ID
          echo VERSION=$VERSION
          docker tag $IMAGE_NAME $IMAGE_ID:$VERSION
          docker push $IMAGE_ID:$VERSION

This builds the image and then I pull and create a container. It runs great. Just curious about the file size differential.

podman pull ghcr.io/jaredhowland/caddy-cf:2.6.4

# Run with the following command:
caddy run --config /etc/caddy/Caddyfile --adapter caddyfile

c. Service/unit/compose file:

Included above.

d. My complete Caddy config:

# The Caddyfile is an easy way to configure your Caddy web server.
#
# Unless the file starts with a global options block, the first
# uncommented line is always the address of your site.
#
# To use your own domain name (with automatic HTTPS), first make
# sure your domain's A/AAAA DNS records are properly pointed to
# this machine's public IP, then replace ":80" below with your
# domain name.

###########################################
## TEMPLATE ###############################
###########################################
(tls) {
	tls email@example.com {
    	dns cloudflare {env.CF_API_TOKEN}
	}
}

(website) {
    root * /var/www/html/{args.0}/public_html

    # Compress files
    encode zstd gzip

    # Enable the static file server.
    file_server

    # Serve a PHP site through php-fpm:
    php_fastcgi php:9000 {
      try_files {path} {path}/index.php =404
    }

    # Use a Caddy template to handle all errors
    handle_errors {
      rewrite * /error.html
      templates
      file_server
    }
}

###########################################
## GET CERTIFICATES #######################
###########################################

*.obscure.cc {
	import tls
}

###########################################
## OBSCURE.CC #############################
###########################################
obscure.cc {
    import tls
    import website "obscure.cc"
}

lib.obscure.cc {
    import website "obscure.cc/lib.obscure.cc"
    # SOME KIND OF REWRITE NEEDS TO HAPPEN HERE
}

Try this:

rewrite /example1.json /app/v1/example1/index.php
rewrite /example2.json /app/v1/example2/index.php

The modern way to do this is set up your PHP app with an index.php at the public webroot, then use a PHP router (for example GitHub - nikic/FastRoute: Fast request router for PHP) to route the request.

This is so simple. I woke up thinking this was going to be the solution but my brain couldn’t get it late last night. Thanks for the explanation. This app is not a priority so I don’t know if we will ever get around to using a PHP router for it but I’m sure we will eventually. In the meantime, this did the trick. Thanks for taking the time to help a noob—this community is amazing!

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.