You can replace this with just handle_path /secure/* instead, which has implicit strip_prefix logic.
You don’t need this at all, Caddy sets it implicitly:
But anyways, header changes the response headers, not request headers.
And the placeholder you’re using here doesn’t make sense. http.matchers.remote_ip is a module name, not a placeholder.
This also doesn’t make sense for similar reasons, but also that placeholder isn’t set until the reverse_proxy handler is executing. If you were to use it in the right place though, i.e. a header_up inside of the reverse_proxy this placeholder would have the value secure_docker_container, i.e. the hostname of the upstream you configured, which is not an IP address.
Ultimately, this isn’t really a problem with Caddy, but rather an issue with Docker. Caddy is not aware of the remote IP because when running with Docker Swarm (as you are, as evidenced by your docker service command), Docker has an ingress proxy which sits between the client and Caddy.
If the ingress proxy supports the PROXY protocol though, you may use the GitHub - mastercactapus/caddy2-proxyprotocol plugin to resolve the issue. You can find docs on configuring listener wrappers here in the docs: