caddyserver / caddy

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
https://caddyserver.com
Apache License 2.0
58.28k stars 4.03k forks source link

Caddy 2.6.2 handling of strip_prefix is broken #5312

Closed krstns closed 1 year ago

krstns commented 1 year ago

Hi,

today I had to update to Caddy 2.6.2 and a config which worked just fine with 2.5.2, stopped.

I've looked at #5073 and #4801 and even though they are closed, it seems they didn't fix it or my use case is different, however I don't see how.

The config in question:

{$SERVER_NAME} {
    log

    route /v1/* {
        uri strip_prefix /v1

        root * /srv/api/public

        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=()"

        php_fastcgi unix//var/run/php/php-fpm.sock {
            env REQUEST_URI {path}{http.matchers.file.remainder}
        }
        encode zstd gzip
        file_server
    }
}

When calling URI /v1/auth/access_token the PHP server is called with /index.php.

I've looked through the docs and it seems there was no change between version 2.5.2 and 2.6.2 regarding strip_prefix. Did something else change?

mholt commented 1 year ago

Thanks for opening an issue! We'll look into this.

It's not immediately clear to me what is going on, so I'll need your help to understand it better.

Ideally, we need to be able to reproduce the bug in the most minimal way possible. This allows us to write regression tests to verify the fix is working. If we can't reproduce it, then you'll have to test our changes for us until it's fixed -- and then we can't add test cases, either.

In particular with your case, it would be helpful to remove php_fastcgi and file_server and instead just use something simple like respond, since php_fastcgi does a lot of fancy things that can lead diagnosis efforts astray.

I've attached a template below that will help make this easier and faster! This will require some effort on your part -- please understand that we will be dedicating time to fix the bug you are reporting if you can just help us understand it and reproduce it easily.

This template will ask for some information you've already provided; that's OK, just fill it out the best you can. :+1: I've also included some helpful tips below the template. Feel free to let me know if you have any questions!

Thank you again for your report, we look forward to resolving it!

Template

## 1. Environment

### 1a. Operating system and version

```
paste here
```

### 1b. Caddy version (run `caddy version` or paste commit SHA)

```
paste here
```

### 1c. Go version (if building Caddy from source; run `go version`)

```
paste here
```

## 2. Description

### 2a. What happens (briefly explain what is wrong)

### 2b. Why it's a bug (if it's not obvious)

### 2c. Log output

```
paste terminal output or logs here
```

### 2d. Workaround(s)

### 2e. Relevant links

## 3. Tutorial (minimal steps to reproduce the bug)

Instructions -- please heed otherwise we cannot help you (help us help you!)

  1. Environment: Please fill out your OS and Caddy versions, even if you don't think they are relevant. (They are always relevant.) If you built Caddy from source, provide the commit SHA and specify your exact Go version.

  2. Description: Describe at a high level what the bug is. What happens? Why is it a bug? Not all bugs are obvious, so convince readers that it's actually a bug.

    • 2c) Log output: Paste terminal output and/or complete logs in a code block. DO NOT REDACT INFORMATION except for credentials. Please enable debug and access logs.
    • 2d) Workaround: What are you doing to work around the problem in the meantime? This can help others who encounter the same problem, until we implement a fix.
    • 2e) Relevant links: Please link to any related issues, pull requests, docs, and/or discussion. This can add crucial context to your report.
  3. Tutorial: What are the minimum required specific steps someone needs to take in order to experience the same bug? Your goal here is to make sure that anyone else can have the same experience with the bug as you do. You are writing a tutorial, so make sure to carry it out yourself before posting it. Please:

    • Start with an empty config. Add only the lines/parameters that are absolutely required to reproduce the bug.
    • Do not run Caddy inside containers.
    • Run Caddy manually in your terminal; do not use systemd or other init systems.
    • If making HTTP requests, avoid web browsers. Use a simpler HTTP client instead, like curl.
    • Do not redact any information from your config (except credentials). Domain names are public knowledge and often necessary for quick resolution of an issue!
    • Note that ignoring this advice may result in delays, or even in your issue being closed. 😞 Only actionable issues are kept open, and if there is not enough information or clarity to reproduce the bug, then the report is not actionable.

Example of a tutorial:

Create a config file: ``` { ... } ``` Open terminal and run Caddy: ``` $ caddy ... ``` Make an HTTP request: ``` $ curl ... ``` Notice that the result is ___ but it should be ___.
francislavoie commented 1 year ago

When calling URI /v1/auth/access_token the PHP server is called with /index.php.

That rewrite is normal. If you don't have a file that exists at /srv/api/public/auth/access_token to serve, then php_fastcgi does a rewrite to /index.php so that your upstream PHP app can route the request. Your PHP app can see the original URL in the REQUEST_URI CGI variable.

Keep in mind that REQUEST_URI has the original URI, not one modified by rewrites, so it will still have /v1 in the URL. This is by design -- the CGI spec requires that it's the original URI as sent from the client. Your app should be made to handle that extra path segment transparently.

krstns commented 1 year ago

Thanks Francis The config (and everything behind it) works unchanged since last June. The only reason I had to update caddy was because of the recent update of Mercure. It seemed like and easy change, since we don't use anything 'special' in caddy (or at least I thought so). The reality was different. Thanks, Matt for the template, I know the issue was written in a shameful way, but I'm going to try to give you more details now. Just bare with me, I've switched from Nginx to Caddy quite recently and it's not going as fast as it should.

krstns commented 1 year ago

1. Environment

1a. Operating system and version

Docker container built using:

ARG CADDY_VERSION=2.6.2

FROM caddy:${CADDY_VERSION}-builder-alpine AS api_platform_caddy_builder

# install Mercure and Vulcain modules
RUN xcaddy build \
    --with github.com/dunglas/mercure \
    --with github.com/dunglas/mercure/caddy \
    --with github.com/caddy-dns/godaddy

1b. Caddy version (run caddy version or paste commit SHA)

v2.6.2 h1:wKoFIxpmOJLGl3QXoo6PNbYvGW4xLEgo32GPBEjWL8o=

1c. Go version (if building Caddy from source; run go version)

go version go1.19.5 linux/amd64

2. Description

2a. What happens (briefly explain what is wrong)

Using uri strip_prefix removes the uri suffix.

2b. Why it's a bug (if it's not obvious)

2c. Log output

See below

2d. Workaround(s)

Adding index.php to the uri.

2e. Relevant links

3. Tutorial (minimal steps to reproduce the bug)

I've conducted several tests to find the best representation of the issues I get. Below are the exact Caddyfiles used for testing, with the redacted domain name and godaddy token. The logs were stripped of domain names and ip addresses.

Without uri strip_prefix

Caddyfile

{
    # Debug
    {$DEBUG}
    debug
    acme_dns godaddy <token>
}

*.my.domain.com, my.domain.com {
    log

    route /* {
        root * /srv/api/public

        header -Access-Control-Allow-Origin
        php_fastcgi unix//var/run/php/php-fpm.sock {
        }
        file_server
    }
}

Request https://my.domain.com/auth/access_token

Caddy debug logs

{"level":"debug","ts":1673902743.0080435,"logger":"http.handlers.rewrite","msg":"rewrote request","request":{"remote_ip":"123.123.123.123","remote_port":"49858","proto":"HTTP/2.0","method":"GET","host":"my.domain.com","uri":"/auth/access_token","headers":{"User-Agent":["insomnia/2022.7.1"],"Cookie":[],"Accept":["*/*"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"my.domain.com"}},"method":"GET","uri":"/index.php"}
{"level":"debug","ts":1673902743.0080967,"logger":"http.handlers.reverse_proxy","msg":"selected upstream","dial":"/var/run/php/php-fpm.sock","total_upstreams":1}
{"level":"debug","ts":1673902743.008194,"logger":"http.reverse_proxy.transport.fastcgi","msg":"roundtrip","request":{"remote_ip":"123.123.123.123","remote_port":"49858","proto":"HTTP/2.0","method":"GET","host":"my.domain.com","uri":"/index.php","headers":{"X-Forwarded-Host":["my.domain.com"],"User-Agent":["insomnia/2022.7.1"],"Cookie":[],"Accept":["*/*"],"X-Forwarded-For":["123.123.123.123"],"X-Forwarded-Proto":["https"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"my.domain.com"}},"env":{"REMOTE_PORT":"49858","REQUEST_SCHEME":"https","SERVER_PORT":"443","HTTP_X_FORWARDED_FOR":"123.123.123.123","REMOTE_ADDR":"123.123.123.123","HTTPS":"on","SSL_PROTOCOL":"TLSv1.3","HTTP_COOKIE":"","REQUEST_METHOD":"GET","SCRIPT_NAME":"/index.php","PATH_INFO":"","DOCUMENT_ROOT":"/srv/api/public","SERVER_SOFTWARE":"Caddy/v2.6.2","SERVER_NAME":"my.domain.com","REQUEST_URI":"/auth/access_token","HTTP_ACCEPT":"*/*","HTTP_X_FORWARDED_HOST":"my.domain.com","REMOTE_HOST":"123.123.123.123","SERVER_PROTOCOL":"HTTP/2.0","REMOTE_USER":"","GATEWAY_INTERFACE":"CGI/1.1","CONTENT_LENGTH":"","QUERY_STRING":"","DOCUMENT_URI":"/index.php","SCRIPT_FILENAME":"/srv/api/public/index.php","HTTP_USER_AGENT":"insomnia/2022.7.1","AUTH_TYPE":"","CONTENT_TYPE":"","HTTP_HOST":"my.domain.com","SSL_CIPHER":"TLS_AES_128_GCM_SHA256","HTTP_X_FORWARDED_PROTO":"https","REMOTE_IDENT":""},"dial":"/var/run/php/php-fpm.sock","env":{"SERVER_PORT":"443","HTTP_X_FORWARDED_FOR":"123.123.123.123","REMOTE_ADDR":"123.123.123.123","REMOTE_PORT":"49858","REQUEST_SCHEME":"https","HTTP_COOKIE":"","REQUEST_METHOD":"GET","HTTPS":"on","SSL_PROTOCOL":"TLSv1.3","PATH_INFO":"","SCRIPT_NAME":"/index.php","SERVER_SOFTWARE":"Caddy/v2.6.2","DOCUMENT_ROOT":"/srv/api/public","HTTP_ACCEPT":"*/*","HTTP_X_FORWARDED_HOST":"my.domain.com","REMOTE_HOST":"123.123.123.123","SERVER_NAME":"my.domain.com","REQUEST_URI":"/auth/access_token","REMOTE_USER":"","SERVER_PROTOCOL":"HTTP/2.0","QUERY_STRING":"","DOCUMENT_URI":"/index.php","SCRIPT_FILENAME":"/srv/api/public/index.php","HTTP_USER_AGENT":"insomnia/2022.7.1","AUTH_TYPE":"","GATEWAY_INTERFACE":"CGI/1.1","CONTENT_LENGTH":"","SSL_CIPHER":"TLS_AES_128_GCM_SHA256","HTTP_X_FORWARDED_PROTO":"https","REMOTE_IDENT":"","CONTENT_TYPE":"","HTTP_HOST":"my.domain.com"},"request":{"remote_ip":"123.123.123.123","remote_port":"49858","proto":"HTTP/2.0","method":"GET","host":"my.domain.com","uri":"/index.php","headers":{"X-Forwarded-Proto":["https"],"X-Forwarded-Host":["my.domain.com"],"User-Agent":["insomnia/2022.7.1"],"Cookie":[],"Accept":["*/*"],"X-Forwarded-For":["123.123.123.123"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"my.domain.com"}}}

PHP Server request

GET https://my.domain.com/auth/access_token

Request https://my.domain.com/index.php/auth/access_token

Caddy debug logs

{"level":"debug","ts":1673902943.632578,"logger":"http.handlers.rewrite","msg":"rewrote request","request":{"remote_ip":"123.123.123.123","remote_port":"49858","proto":"HTTP/2.0","method":"GET","host":"my.domain.com","uri":"/index.php/auth/access_token","headers":{"User-Agent":["insomnia/2022.7.1"],"Cookie":[],"Accept":["*/*"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"my.domain.com"}},"method":"GET","uri":"/index.php"}
{"level":"debug","ts":1673902943.6326165,"logger":"http.handlers.reverse_proxy","msg":"selected upstream","dial":"/var/run/php/php-fpm.sock","total_upstreams":1}
{"level":"debug","ts":1673902943.632731,"logger":"http.reverse_proxy.transport.fastcgi","msg":"roundtrip","request":{"remote_ip":"123.123.123.123","remote_port":"49858","proto":"HTTP/2.0","method":"GET","host":"my.domain.com","uri":"/index.php","headers":{"User-Agent":["insomnia/2022.7.1"],"Cookie":[],"Accept":["*/*"],"X-Forwarded-For":["123.123.123.123"],"X-Forwarded-Proto":["https"],"X-Forwarded-Host":["my.domain.com"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"my.domain.com"}},"env":{"REMOTE_IDENT":"","CONTENT_TYPE":"","QUERY_STRING":"","REQUEST_METHOD":"GET","HTTP_HOST":"my.domain.com","SERVER_PORT":"443","HTTP_X_FORWARDED_FOR":"123.123.123.123","HTTP_X_FORWARDED_PROTO":"https","REQUEST_SCHEME":"https","SSL_CIPHER":"TLS_AES_128_GCM_SHA256","CONTENT_LENGTH":"","PATH_INFO":"/auth/access_token","REMOTE_HOST":"123.123.123.123","SCRIPT_NAME":"/index.php","HTTP_X_FORWARDED_HOST":"my.domain.com","SERVER_SOFTWARE":"Caddy/v2.6.2","DOCUMENT_URI":"/index.php","SSL_PROTOCOL":"TLSv1.3","HTTP_USER_AGENT":"insomnia/2022.7.1","DOCUMENT_ROOT":"/srv/api/public","SCRIPT_FILENAME":"/srv/api/public/index.php","PATH_TRANSLATED":"/srv/api/public/auth/access_token","HTTPS":"on","AUTH_TYPE":"","GATEWAY_INTERFACE":"CGI/1.1","REMOTE_USER":"","SERVER_PROTOCOL":"HTTP/2.0","HTTP_COOKIE":"","HTTP_ACCEPT":"*/*","REMOTE_ADDR":"123.123.123.123","REMOTE_PORT":"49858","SERVER_NAME":"my.domain.com","REQUEST_URI":"/index.php/auth/access_token"},"dial":"/var/run/php/php-fpm.sock","env":{"REQUEST_METHOD":"GET","HTTP_HOST":"my.domain.com","SERVER_PORT":"443","HTTP_X_FORWARDED_FOR":"123.123.123.123","HTTP_X_FORWARDED_PROTO":"https","QUERY_STRING":"","SSL_CIPHER":"TLS_AES_128_GCM_SHA256","REQUEST_SCHEME":"https","PATH_INFO":"/auth/access_token","REMOTE_HOST":"123.123.123.123","SCRIPT_NAME":"/index.php","HTTP_X_FORWARDED_HOST":"my.domain.com","CONTENT_LENGTH":"","DOCUMENT_URI":"/index.php","SSL_PROTOCOL":"TLSv1.3","HTTP_USER_AGENT":"insomnia/2022.7.1","SERVER_SOFTWARE":"Caddy/v2.6.2","SCRIPT_FILENAME":"/srv/api/public/index.php","PATH_TRANSLATED":"/srv/api/public/auth/access_token","HTTPS":"on","DOCUMENT_ROOT":"/srv/api/public","GATEWAY_INTERFACE":"CGI/1.1","REMOTE_USER":"","SERVER_PROTOCOL":"HTTP/2.0","HTTP_COOKIE":"","HTTP_ACCEPT":"*/*","AUTH_TYPE":"","REMOTE_PORT":"49858","SERVER_NAME":"my.domain.com","REQUEST_URI":"/index.php/auth/access_token","REMOTE_ADDR":"123.123.123.123","CONTENT_TYPE":"","REMOTE_IDENT":""},"request":{"remote_ip":"123.123.123.123","remote_port":"49858","proto":"HTTP/2.0","method":"GET","host":"my.domain.com","uri":"/index.php","headers":{"User-Agent":["insomnia/2022.7.1"],"Cookie":[],"Accept":["*/*"],"X-Forwarded-For":["123.123.123.123"],"X-Forwarded-Proto":["https"],"X-Forwarded-Host":["my.domain.com"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"my.domain.com"}}}

PHP Server request

GET https://my.domain.com/index.php/auth/access_token

With uri strip_prefix

Caddyfile

{
    # Debug
    {$DEBUG}
    debug
    acme_dns godaddy <token>
}

*.my.domain.com, my.domain.com {
    log

    route /* {
        uri strip_prefix /v1

        root * /srv/api/public

        header -Access-Control-Allow-Origin
        php_fastcgi unix//var/run/php/php-fpm.sock {
            env REQUEST_URI {path}{http.matchers.file.remainder}
        }
        file_server
    }
}

Request https://my.domain.com/v1/index.php/auth/access_token

Caddy debug logs

{"level":"debug","ts":1673903167.6452668,"logger":"http.handlers.rewrite","msg":"rewrote request","request":{"remote_ip":"123.123.123.123","remote_port":"49985","proto":"HTTP/2.0","method":"GET","host":"my.domain.com","uri":"/v1/index.php/auth/access_token","headers":{"User-Agent":["insomnia/2022.7.1"],"Cookie":[],"Accept":["*/*"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"my.domain.com"}},"method":"GET","uri":"/index.php/auth/access_token"}
{"level":"debug","ts":1673903167.6454046,"logger":"http.handlers.rewrite","msg":"rewrote request","request":{"remote_ip":"123.123.123.123","remote_port":"49985","proto":"HTTP/2.0","method":"GET","host":"my.domain.com","uri":"/index.php/auth/access_token","headers":{"User-Agent":["insomnia/2022.7.1"],"Cookie":[],"Accept":["*/*"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"my.domain.com"}},"method":"GET","uri":"/index.php"}
{"level":"debug","ts":1673903167.6454287,"logger":"http.handlers.reverse_proxy","msg":"selected upstream","dial":"/var/run/php/php-fpm.sock","total_upstreams":1}
{"level":"debug","ts":1673903167.6455858,"logger":"http.reverse_proxy.transport.fastcgi","msg":"roundtrip","request":{"remote_ip":"123.123.123.123","remote_port":"49985","proto":"HTTP/2.0","method":"GET","host":"my.domain.com","uri":"/index.php","headers":{"Cookie":[],"Accept":["*/*"],"User-Agent":["insomnia/2022.7.1"],"X-Forwarded-For":["123.123.123.123"],"X-Forwarded-Proto":["https"],"X-Forwarded-Host":["my.domain.com"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"my.domain.com"}},"env":{"REQUEST_SCHEME":"https","HTTP_COOKIE":"","HTTP_USER_AGENT":"insomnia/2022.7.1","HTTPS":"on","HTTP_X_FORWARDED_PROTO":"https","HTTP_X_FORWARDED_HOST":"my.domain.com","CONTENT_LENGTH":"","CONTENT_TYPE":"","REMOTE_PORT":"49985","SERVER_SOFTWARE":"Caddy/v2.6.2","SERVER_NAME":"my.domain.com","HTTP_X_FORWARDED_FOR":"123.123.123.123","SSL_CIPHER":"TLS_AES_128_GCM_SHA256","PATH_INFO":"/auth/access_token","DOCUMENT_URI":"/index.php","REQUEST_URI":"/index.php/auth/access_token","SCRIPT_FILENAME":"/srv/api/public/index.php","DOCUMENT_ROOT":"/srv/api/public","HTTP_HOST":"my.domain.com","PATH_TRANSLATED":"/srv/api/public/auth/access_token","QUERY_STRING":"","REMOTE_HOST":"123.123.123.123","SSL_PROTOCOL":"TLSv1.3","HTTP_ACCEPT":"*/*","REQUEST_METHOD":"GET","SCRIPT_NAME":"/index.php","AUTH_TYPE":"","REMOTE_IDENT":"","REMOTE_ADDR":"123.123.123.123","REMOTE_USER":"","GATEWAY_INTERFACE":"CGI/1.1","SERVER_PROTOCOL":"HTTP/2.0","SERVER_PORT":"443"},"dial":"/var/run/php/php-fpm.sock","env":{"HTTP_HOST":"my.domain.com","PATH_TRANSLATED":"/srv/api/public/auth/access_token","DOCUMENT_ROOT":"/srv/api/public","REMOTE_HOST":"123.123.123.123","SSL_PROTOCOL":"TLSv1.3","HTTP_ACCEPT":"*/*","QUERY_STRING":"","REMOTE_IDENT":"","REMOTE_ADDR":"123.123.123.123","REMOTE_USER":"","REQUEST_METHOD":"GET","SCRIPT_NAME":"/index.php","AUTH_TYPE":"","SERVER_PROTOCOL":"HTTP/2.0","SERVER_PORT":"443","GATEWAY_INTERFACE":"CGI/1.1","HTTP_COOKIE":"","HTTP_USER_AGENT":"insomnia/2022.7.1","REQUEST_SCHEME":"https","CONTENT_TYPE":"","REMOTE_PORT":"49985","SERVER_SOFTWARE":"Caddy/v2.6.2","HTTPS":"on","HTTP_X_FORWARDED_PROTO":"https","HTTP_X_FORWARDED_HOST":"my.domain.com","CONTENT_LENGTH":"","HTTP_X_FORWARDED_FOR":"123.123.123.123","SERVER_NAME":"my.domain.com","DOCUMENT_URI":"/index.php","REQUEST_URI":"/index.php/auth/access_token","SCRIPT_FILENAME":"/srv/api/public/index.php","SSL_CIPHER":"TLS_AES_128_GCM_SHA256","PATH_INFO":"/auth/access_token"},"request":{"remote_ip":"123.123.123.123","remote_port":"49985","proto":"HTTP/2.0","method":"GET","host":"my.domain.com","uri":"/index.php","headers":{"X-Forwarded-Host":["my.domain.com"],"Cookie":[],"Accept":["*/*"],"User-Agent":["insomnia/2022.7.1"],"X-Forwarded-For":["123.123.123.123"],"X-Forwarded-Proto":["https"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"my.domain.com"}}}

PHP Server request

GET https://my.domain.com/index.php/auth/access_token

Request https://my.domain.com/v1/auth/access_token

Caddy debug logs

{"level":"debug","ts":1673903222.8733582,"logger":"http.handlers.rewrite","msg":"rewrote request","request":{"remote_ip":"123.123.123.123","remote_port":"49985","proto":"HTTP/2.0","method":"GET","host":"my.domain.com","uri":"/v1/auth/access_token","headers":{"User-Agent":["insomnia/2022.7.1"],"Cookie":[],"Accept":["*/*"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"my.domain.com"}},"method":"GET","uri":"/auth/access_token"}
{"level":"debug","ts":1673903222.8735282,"logger":"http.handlers.rewrite","msg":"rewrote request","request":{"remote_ip":"123.123.123.123","remote_port":"49985","proto":"HTTP/2.0","method":"GET","host":"my.domain.com","uri":"/auth/access_token","headers":{"User-Agent":["insomnia/2022.7.1"],"Cookie":[],"Accept":["*/*"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"my.domain.com"}},"method":"GET","uri":"/index.php"}
{"level":"debug","ts":1673903222.8735452,"logger":"http.handlers.reverse_proxy","msg":"selected upstream","dial":"/var/run/php/php-fpm.sock","total_upstreams":1}
{"level":"debug","ts":1673903222.8736315,"logger":"http.reverse_proxy.transport.fastcgi","msg":"roundtrip","request":{"remote_ip":"123.123.123.123","remote_port":"49985","proto":"HTTP/2.0","method":"GET","host":"my.domain.com","uri":"/index.php","headers":{"User-Agent":["insomnia/2022.7.1"],"Cookie":[],"Accept":["*/*"],"X-Forwarded-For":["123.123.123.123"],"X-Forwarded-Proto":["https"],"X-Forwarded-Host":["my.domain.com"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"my.domain.com"}},"env":{"HTTP_X_FORWARDED_HOST":"my.domain.com","REQUEST_URI":"/index.php","SCRIPT_NAME":"/index.php","HTTPS":"on","HTTP_ACCEPT":"*/*","REMOTE_ADDR":"123.123.123.123","REMOTE_HOST":"123.123.123.123","SERVER_NAME":"my.domain.com","DOCUMENT_URI":"/index.php","HTTP_HOST":"my.domain.com","HTTP_USER_AGENT":"insomnia/2022.7.1","CONTENT_LENGTH":"","DOCUMENT_ROOT":"/srv/api/public","HTTP_COOKIE":"","HTTP_X_FORWARDED_PROTO":"https","REMOTE_PORT":"49985","SCRIPT_FILENAME":"/srv/api/public/index.php","HTTP_X_FORWARDED_FOR":"123.123.123.123","REQUEST_SCHEME":"https","SERVER_SOFTWARE":"Caddy/v2.6.2","SERVER_PORT":"443","SSL_PROTOCOL":"TLSv1.3","SERVER_PROTOCOL":"HTTP/2.0","SSL_CIPHER":"TLS_AES_128_GCM_SHA256","GATEWAY_INTERFACE":"CGI/1.1","PATH_INFO":"","REMOTE_USER":"","REQUEST_METHOD":"GET","AUTH_TYPE":"","REMOTE_IDENT":"","CONTENT_TYPE":"","QUERY_STRING":""},"dial":"/var/run/php/php-fpm.sock","env":{"AUTH_TYPE":"","REMOTE_IDENT":"","CONTENT_TYPE":"","QUERY_STRING":"","HTTP_X_FORWARDED_HOST":"my.domain.com","HTTP_ACCEPT":"*/*","REMOTE_ADDR":"123.123.123.123","REMOTE_HOST":"123.123.123.123","SERVER_NAME":"my.domain.com","DOCUMENT_URI":"/index.php","REQUEST_URI":"/index.php","SCRIPT_NAME":"/index.php","HTTPS":"on","HTTP_HOST":"my.domain.com","HTTP_USER_AGENT":"insomnia/2022.7.1","CONTENT_LENGTH":"","DOCUMENT_ROOT":"/srv/api/public","HTTP_COOKIE":"","HTTP_X_FORWARDED_PROTO":"https","REMOTE_PORT":"49985","SCRIPT_FILENAME":"/srv/api/public/index.php","REQUEST_SCHEME":"https","SERVER_SOFTWARE":"Caddy/v2.6.2","SERVER_PORT":"443","SSL_PROTOCOL":"TLSv1.3","HTTP_X_FORWARDED_FOR":"123.123.123.123","GATEWAY_INTERFACE":"CGI/1.1","PATH_INFO":"","REMOTE_USER":"","REQUEST_METHOD":"GET","SERVER_PROTOCOL":"HTTP/2.0","SSL_CIPHER":"TLS_AES_128_GCM_SHA256"},"request":{"remote_ip":"123.123.123.123","remote_port":"49985","proto":"HTTP/2.0","method":"GET","host":"my.domain.com","uri":"/index.php","headers":{"User-Agent":["insomnia/2022.7.1"],"Cookie":[],"Accept":["*/*"],"X-Forwarded-For":["123.123.123.123"],"X-Forwarded-Proto":["https"],"X-Forwarded-Host":["my.domain.com"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"my.domain.com"}}}

PHP Server request

GET https://my.domain.com/index.php/

As you can see, with the only difference of the strip_prefix the behaviour is different.
I ignore the impact of the env REQUEST_URI, since it's not part of the request resolution, but it is required for the PHP server to have the right uri without the original /v1 in the request.

For completeness, the Dockerfile I use for building caddy:

francislavoie commented 1 year ago

Well, what also matters is what exists on disk. Do you have a /srv/api/public/index.php and/or /srv/api/public/v1/index.php?

krstns commented 1 year ago

I only have /srv/api/public/index.php, this config has not changed at all for me. I mean... it was never inside a v1 directory. Also, after adding index.php to the URL the v1 is properly removed and the /srv/api/public/index.php is used.

krstns commented 1 year ago

copying content from /srv/api/public to /src/api/public/v1/ changed nothing I'm afraid. index.php is there but the behaviour didn't change.

Caddy logs still show: "SCRIPT_FILENAME":"/srv/api/public/index.php"

krstns commented 1 year ago

Is there any workaround I could use? Currently I can add index.php to the path, but then PHP server sees this and it affects a lot of functionality. For example, in api-platform all API ids have index.php inside.

francislavoie commented 1 year ago

Sorry, I don't have time right now to look deeper into this, but I will as soon as I can. I'll need to play around with this to trace what's going on with the rewrites and paths, before I can give a more informed answer.

krstns commented 1 year ago

I'm still fighting with it. What I did find is that when using uri strip_prefix and setting up env REQUEST_URI does not work together. regardless of how I try to set it, the REQUEST_URI variable ends up being index.php. If I use respond {path} it responds with the correct, stripped path.

EDIT: Another find. It's not that the REQUEST_URI is not set correctly, it seems. It's that the variable values are incorrect. If I do env REQUEST_URI /auth/access_token then it works fine, while env REQUEST_URI {path} will end up with REQUEST_URI=index.php. Another fun thing. sending {path} in respond is ok.

krstns commented 1 year ago

I was able to work around this issue by creating my own variable outside of php_fastcgi in the following way:

        vars myPath {path}{http.matchers.file.remainder}

        php_fastcgi unix//var/run/php/php-fpm.sock {
            env REQUEST_URI {vars.myPath}
        }
francislavoie commented 1 year ago

Well, php_fastcgi itself does rewrites before doing the proxying and setting envs. So the file match remainder comes from the try_files inside of php_fastcgi.

But I'll take a closer look soon, when I'm less busy.

francislavoie commented 1 year ago

Sorry for the delay on this. Forgot to come back to this.

I think I'll close this. There's no bug here, really. Like I said earlier, REQUEST_URI is supposed to be the original unmodified URI from the client. If you need to break convention, then it's your responsibility to configure Caddy to do so.

Your workaround using vars seems the most sensible. FWIW, this is the simplest and most accurate way to do it:

example.com {
    root * /srv
    handle_path /v1* {
        vars rewritten_uri {uri}
        php_fastcgi php:9000 {
            env REQUEST_URI {vars.rewritten_uri}
        }
    }
    file_server
}

Using {uri} is the most correct because it's the URI (both path and query) that's passed in REQUEST_URI, not just the path. Doing it this way means that rewritten_uri gets the value from current URI after having been rewritten by handle_path (which has built-in strip_prefix logic). And this will overwrite the "original URI" with the stripped URI.