Weird 302 redirects for requests and context canceled

1. The problem I’m having:

Sometimes with no reason i got 302 redirect from /api/ to root of the site /
or regular context canceled

Its start to happen after i upgrade from Caddy v1
my previous config file

dev.testsite.net/api {
    proxy / web-api-gateway:8188 {
            transparent
            websocket
            header_downstream Access-Control-Allow-Origin "https://dev.testsite.net"
            header_downstream Access-Control-Allow-Credentials "true"
        }
    errors stderr
}

dev.testsite.net/auth {
    proxy / auth-service:9191 {
            transparent
            header_downstream Access-Control-Allow-Origin "https://dev.testsite.net"
            header_downstream Access-Control-Allow-Credentials "true"
        }
    errors stderr
}

dev.testsite.net  {
    root /www/html
    rewrite {
      if    {path} not_match ^\/0.0.0.0
      to    {path} {path}/ /?_url={uri}
    }
    errors stderr
}

Is everything ok with my v2 configuration file ? maybe some recommendation?

2. Error messages and/or full log output:

ERR ts=1698908215.2266824 logger=http.handlers.reverse_proxy msg=aborting with incomplete response upstream=web-api-gateway:8188 duration=2.260684777 request={"remote_ip":"10.0.0.2","remote_port":"58748","client_ip":"10.0.0.2","proto":"HTTP/2.0","method":"GET","host":"dev.testsite.net","uri":"/v1/filter/installation-tree?pageSize=999999","headers":{"Sec-Ch-Ua-Platform":["\"Windows\""],"Cache-Control":["no-cache"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["10.0.0.2"],"Sec-Fetch-Dest":["empty"],"User-Agent":["Mozilla/5.0 (Alex; Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36"],"Accept-Language":["en,ru;q=0.9,en-US;q=0.8,en-GB;q=0.7"],"Cookie":[],"Sec-Ch-Ua-Mobile":["?0"],"Authorization":[],"Sec-Fetch-Site":["same-origin"],"X-Forwarded-Proto":["https"],"X-Forwarded-Host":["dev.testsite.net"],"Pragma":["no-cache"],"Content-Type":["application/json"],"Referer":["https://dev.testsite.net/dashboard/analytics-expenses"],"Sec-Ch-Ua":["\"Chromium\";v=\"118\", \"Google Chrome\";v=\"118\", \"Not=A?Brand\";v=\"99\""],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br"]},"tls":{"resumed":true,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"dev.testsite.net"}} error=reading: context canceled
ERR ts=1698908915.760871 logger=http.handlers.reverse_proxy msg=reading from backend error=read tcp 10.0.5.54:38676->10.0.5.142:8188: read: connection reset by peer
ERR ts=1698908915.7609837 logger=http.handlers.reverse_proxy msg=aborting with incomplete response upstream=web-api-gateway:8188 duration=0.01198845 request={"remote_ip":"10.0.0.2","remote_port":"4299","client_ip":"10.0.0.2","proto":"HTTP/1.1","method":"POST","host":"dev.testsite.net","uri":"/websocket/263/hi4w34tw/xhr_streaming?access_token=46b98d9c-c505-4285-910e-2bcae2d96c2c&t=1698908490114","headers":{"User-Agent":["Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36"],"Sec-Ch-Ua-Mobile":["?0"],"Accept-Language":["ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7"],"Sec-Fetch-Site":["same-origin"],"Accept":["*/*"],"Content-Length":["0"],"Referer":["https://localhost:3000/dashboard/passportization"],"X-Forwarded-For":["10.0.0.2"],"X-Forwarded-Proto":["https"],"X-Forwarded-Host":["dev.testsite.net"],"Accept-Encoding":["gzip, deflate, br"],"Origin":["https://localhost:3000"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Sec-Ch-Ua-Platform":["\"macOS\""],"Cookie":[],"Sec-Ch-Ua":["\"Google Chrome\";v=\"117\", \"Not;A=Brand\";v=\"8\", \"Chromium\";v=\"117\""]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"","server_name":"dev.testsite.net"}} error=reading: read tcp 10.0.5.54:38676->10.0.5.142:8188: read: connection reset by peer

3. Caddy version:

[v2.7.5] (Release v2.7.5 · caddyserver/caddy · GitHub)

4. How I installed and ran Caddy:

I follow this links to install caddy

a. System environment:

Ubuntu 22.02
DockerSwarm
react
websockets

b. Command:

caddy start

c. Service/unit/compose file:

PASTE OVER THIS, BETWEEN THE ``` LINES.
Please use the preview pane to ensure it looks nice.

d. My complete Caddy config:

dev.testsite.net {
    # setting X-Frame-Options
    header / {
        X-Frame-Options "SAMEORIGIN"
    }

       # route for API
    handle /api/* {
        uri strip_prefix /api
        reverse_proxy web-api-gateway:8188 {
            header_down Access-Control-Allow-Origin "https://dev.testsite.net"
            header_down Access-Control-Allow-Credentials "true"
        }
    }

    # route for authentication-related requests (including /forgot-password)
    handle /auth/* {
        uri strip_prefix /auth
        reverse_proxy auth-service:9191 {
            header_down Access-Control-Allow-Origin "https://dev.testsite.net"
            header_down Access-Control-Allow-Credentials "true"
        }
    }

    # Serve static files from /www/html
    handle_path /static/* {
        root * /www/html/static
    header Cache-Control "public, max-age=3600"
        file_server
    }

    # Handle requests to react-app
    handle  {
        root * /www/html
        try_files {path} /index.html
      file_server
  }
    handle_errors {
        @uploadNotExists {
            path /*
            not file # not stonks
        }
        redir @uploadNotExists /
    }
     encode gzip zstd {
        minimum_length 4096
    }
}

5. Links to relevant resources:

Probably because you used uri strip_prefix. Your upstream app won’t be aware that there should be an /api base path, so it doesn’t know that it needs to make URLs with /api in them.

That’s usually when the client closes the connection before Caddy was done writing out. It’s usually not a problem. If things seem to work okay, then don’t worry about it.

You can simplify this by using handle_path instead which has built-in uri strip_prefix behaviour, saving you a line of config.

FYI this will only set that header for requests to exactly / and nothing else. If you want that applied to all requests, do this instead:

header X-Frame-Options "SAMEORIGIN"

Also, please run caddy fmt -o on your Caddyfile to clean up the formatting. The whitespace is quite messy and it makes it hard to read since the { } braces aren’t aligned.

1 Like

Thank you for reply

Did I understand correctly that i should try to change directive handle to handle_path like below?

# route for API
handle_path /api/* {
    reverse_proxy web-api-gateway:8188 {
        header_down Access-Control-Allow-Origin "https://dev.testsite.net"
        header_down Access-Control-Allow-Credentials "true"
    }
}

Yes, but that won’t change behaviour, it’s identical, just one line shorter.

I can’t explain the redirects because you haven’t shown evidence of them, but like I said earlier my assumption is that because you’re stripping /api (either with uri strip_prefix or handle_path, both do it) your upstream app isn’t aware that the original request had /api so when it makes URLs it redirects to / instead of /api. Basically you need to configure your API app to be aware that it’s being run under a subpath for its URL building/redirect logic.

Its looks like the error handles configures incorrect , when we have 502 error(service restart or warmup) we can see 302 redirect to the route /