Redirect from custom port to custom page

1. Output of caddy version:

v2.6.2

2. How I run Caddy:

As a service to store a website with Docker containers

a. System environment:

Linux version 22.04, Docker version 20.10.20

b. Command:

c. Service/unit/compose file:

FROM python:3.7
WORKDIR /app

RUN pip install --upgrade pip
COPY requirements.txt ./requirements.txt
RUN pip install --user -r requirements.txt
EXPOSE 8501

COPY . /app
ENTRYPOINT ["streamlit", "run"]
CMD ["main.py", "--server.port=8501"]

d. My complete Caddy config:

site. com {
    root * /var/www/site. com
    encode gzip
    file_server

    tls {
        dns digitalocean {env.DO_AUTH_TOKEN}
    }

    reverse_proxy localhost:8501
    reverse_proxy :8501 /project1
}

3. The problem I’m having:

Hello everyone, can you please write how to deploy the app on Caddy + Docker on custom url and https?

  1. From http://site:8501/ to https://site/project1 (or https://project1.site) (need to allow https on a custom port). CORS = False

With this Caddyfile it’s write the error on a /project1 page, but run on a main site page.

4. Error messages and/or full log output:

You have requested page /project1, but no corresponding file was found in the app's pages/ directory. Running the app's main page.

5. What I already tried:

Not working stackoverflow solution, cause I open 8501/8502 ports when deploy a Docker containers, and can’t use it in Caddyfile cause address already in use.
Looks like all of this can be made in Caddyfile with reverse_proxy, redir or rewrite

6. Links to relevant resources:

There’s a lot of syntax issues in your config.

You can’t have spaces in your domain name.

Same here, you can’t have spaces in your root path (unless you wrap it in quotes, and you legitimately do have spaces in the path on disk).

This doesn’t make sense. Path matchers must be the first argument to a directive. With this, Caddy will think you mean that :8501 and /project1 are two upstreams and that it should load balance between the two. But that makes no sense because /project1 is most definitely not an upstream.

Finally, you have both reverse_proxy and file_server in your config, but aren’t using matchers to tell which to run when. So according to the directive order, reverse_proxy will always take precedence over file_server, and all requests will be sent to your configured upstream.

So you’re running a Python app in Docker. Are you also running Caddy in Docker, or are you running Caddy on the host?

Did you bind port 8501 of your Python container to the host? How did you run this container?

This matters, because if you’re running Caddy in Docker, then reverse_proxy localhost:8501 will not work because localhost in Docker means “this container”, so Caddy wouldn’t be able to connect to anything (instead, you’d need to do reverse_proxy my-python-app:8501, i.e. the Docker container’s name/service).

If you’re running Caddy on the host and you did bind your Python container’s port to the host, then reverse_proxy localhost:8501 should work.

With that said, in your title you wrote “redirect”. Are you sure that’s what you mean? Or do you mean to “proxy”? Those are different concepts. A redirect is a special type of HTTP response that tells the client (browser) to “try again at a different URL” (and it changes the URL in the address bar). A proxy’s job is to take the incoming request, send it to another server, get the response, and send that back to the client.

1 Like

@francislavoie hello, thanks for the answer. The “site. com” syntax is just to pass the forum limitations about “Sorry, you can’t post the word ‘site dot com’; it’s not allowed.”, so originally I write site like “site dot com”.

So you’re running a Python app in Docker. Are you also running Caddy in Docker, or are you running Caddy on the host?

I’m running Caddy on the host, cause it is more flexible for me to modify Caddyfile without building container.

Did you bind port 8501 of your Python container to the host? How did you run this container?

With Docker run. Like “docker run --restart=always -d -p 8501:8501 streamlit”.

With that said, in your title you wrote “redirect”. Are you sure that’s what you mean? Or do you mean to “proxy”? Those are different concepts. A redirect is a special type of HTTP response that tells the client (browser) to “try again at a different URL” (and it changes the URL in the address bar). A proxy’s job is to take the incoming request, send it to another server, get the response, and send that back to the client.

The main task is to allow using https and store app on a custom page. So from working config of http://site dot com:port, to https://site dot com/project, or https://project.site dot com. In this case we modify the URL, so looks like redirect. But I’m not so familiar with it, that is why I’m asking some forum advice. Could you please write what should I change in Caddyfile in this case (or maybe a better solution how to get the result). Thanks!

So you’re saying you need it to be accessed three different ways? Why not just one? What’s the purpose of that?

Your explanation isn’t very clear what the goal is exactly, so I don’t know what to suggest. Please elaborate.

Hi, I need only 1 way of access (what is simpler).
The main goal is to move from http and custom port to https and suitable site address.
So, from http://site. com:8501 to ONE of this variants: https://site .com/project OR https://project.site. com.
I can write in more details, if it’s needed. Thanks

This should work fine, if you only need one way to access your site:

project.example.com {
	reverse_proxy localhost:8501
}

I still don’t understand what you’re unclear on.

Hello, it’s not working. The original site (example.com:8501) working, the project.example.com redirect to the site’s main page (example.com). Here is Caddyfile (both of 2 variants not working)

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

example.com {
    root * /var/www/example.com
    encode gzip
    file_server

    tls {
        dns digitalocean {env.DO_AUTH_TOKEN}
    }
}

project.example.com {
        reverse_proxy localhost:8501
}


#project.example.com {
 #       reverse_proxy example.com:8501
#}

This tells us nothing.

Please be specific. What’s the symptom? It could be an infinite number of reasons. We can’t guess unless you show us what is actually happening.

What’s in your logs? What did you try that makes you think it’s not working?

2 Likes

Hello, the main reason I think that all subdomens redirect to main page (*.example.com → example.com) and I don’t understand what’s wrong. Here is Caddyfile

#www.example.com, www.example.com/ {
#        redir https://example.com{uri}
#}

example.com {
    root * /var/www/example.com
    encode gzip
    file_server

    tls {
        dns digitalocean {env.DO_AUTH_TOKEN}
    }
}

project.example.com {
        reverse_proxy localhost:8501
}

Maybe there is some template to provide as output, I am interested to solve this question. Thanks

Caddy isn’t the one who redirects your *.example.com subdomains to example.com.
There is no default behaviour like that in Caddy or whatsoever.

There is reason to believe that those redirect must be happening in your upstream (localhost:8501).

Please provide more details, as @francislavoie already requested.

Provide logs, rephrase what is happening and what you expect should be happening instead.
Also, running and sharing some curl --verbose -L project.example.com requests are usually of great help and provide very insightful details. This is applies to you especially, since you are redacting your actual domain name - which makes helping you even more difficult.


tl;dr: we need more infos to be able to help you.

2 Likes

Okay, thanks for details.
Introduction: I’m hosting a website on VPS, that can be accessed using IP or domain name.
In this site I use Docker for some projects to show by URL.
Docker container deploy in example.com:8501 (docker run --restart=always -d -p 8501:8501 streamlit) using http only. I want to have an URL: https://project.example.com.

Here is current Caddyfile:

#www.example.com, www.example.com/ {
#        redir https://example.com{uri}
#}

example.com {
    root * /var/www/example.com
    encode gzip
    file_server

    tls {
        dns digitalocean {env.DO_AUTH_TOKEN}
    }
}

project.example.com {
        reverse_proxy http://SITE IP:8501
}

Al so I’ve tried @francislavoie solution:

project.example.com {
	reverse_proxy localhost:8501
}

The main problem is that https://project.example.com redirects to https://example.com, http://example.com:8501 works without any redirects.

In my expectations after setting Caddyfile it should be deployed from http://example.com:8501 to https://project.example.com and works at least in https://project.example.com (or maybe also in http://example.com:8501).

Here is the log of curl --verbose -L project.example.com:

Logs
root@root:~# curl --verbose -L project.example.com
*   Trying IP1:80...
* Connected to project.example.com (IP1) port 80 (#0)
> GET / HTTP/1.1
> Host: project.example.com
> User-Agent: curl/7.81.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 307 Temporary Redirect
< Server: openresty
< Date: Sun, 30 Oct 2022 10:17:21 GMT
< Content-Type: text/html; charset=utf-8
< Content-Length: 168
< Connection: keep-alive
< Location: http://example.com
< X-Frame-Options: sameorigin
<
* Ignoring the response-body
* Connection #0 to host project.example.com left intact
* Issue another request to this URL: 'http://example.com/'
*   Trying SITE IP:80...
* Connected to example.com (SITE IP) port 80 (#1)
> GET / HTTP/1.1
> Host: example.com
> User-Agent: curl/7.81.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 308 Permanent Redirect
< Connection: close
< Location: https://example.com/
< Server: Caddy
< Date: Sun, 30 Oct 2022 10:17:21 GMT
< Content-Length: 0
<
* Closing connection 1
* Clear auth, redirects to port from 80 to 443
* Issue another request to this URL: 'https://example.com/'
*   Trying SITE IP:443...
* Connected to example.com (SITE IP) port 443 (#2)
* ALPN, offering h2
* ALPN, offering http/1.1
*  CAfile: /etc/ssl/certs/ca-certificates.crt
*  CApath: /etc/ssl/certs
* TLSv1.0 (OUT), TLS header, Certificate Status (22):
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS header, Certificate Status (22):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS header, Finished (20):
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.2 (OUT), TLS header, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_128_GCM_SHA256
* ALPN, server accepted to use h2
* Server certificate:
*  subject: CN=example.com
*  start date: Oct 23 04:02:42 2022 GMT
*  expire date: Jan 21 04:02:41 2023 GMT
*  subjectAltName: host "example.com" matched cert's "example.com"
*  issuer: C=US; O=Let's Encrypt; CN=R3
*  SSL certificate verify ok.
* Using HTTP2, server supports multiplexing
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=                                                                                                              0
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
* Using Stream ID: 1 (easy handle value)
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
> GET / HTTP/2
> Host: example.com
> user-agent: curl/7.81.0
> accept: */*
>
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* Connection state changed (MAX_CONCURRENT_STREAMS == 250)!
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* TLSv1.2 (IN), TLS header, Supplemental data (23):
< HTTP/2 200
< accept-ranges: bytes
< alt-svc: h3=":443"; ma=values
< content-type: text/html; charset=utf-8
< etag: "tagvalue"
< last-modified: Sat, 22 Oct 2022 19:02:12 GMT
< server: Caddy
< content-length: 11552
< date: Sun, 30 Oct 2022 10:17:21 GMT
<
* TLSv1.2 (IN), TLS header, Supplemental data (23):
<-- SITE CODE -->
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* Connection #2 to host example.com left intact

The thing that I notice is that IP1 was blocked in my country, but maybe it’s not related in this topic.

I also have 2 A records in domain management site with SITE IP to example.com and www.example.com, 1 CNAME record and 3 NS records.

1 Like

Thank you for providing the curl output.

Caddy isn’t the one redirecting project.example.com to example.com.
Some other server running openresty is.
You either set up something in the past or your domain registrar is doing that.
I know that porkbun is using openresty for their redirect service.

Essentially:
The DNS for project.example.com is pointing to some other server, that does the redirect. You will have to remove that and point a record to Caddy, just like you did for example.com.

Note: You maybe need to wait awhile because of DNS caching.

After that, you can use @francislavoie Caddyfile portion:


PS: Consider using docker run --restart=always -d -p 127.0.0.1:8501:8501 streamlit, which binds your post :8501 to localhost only, so your service is only accessible via Caddy using your domain configured.

1 Like

Hello, yes, I’m using this domain registrar. Is it change the solution you provided?

I’m not sure what kind of records to remove:

A	example.com	SITE IP	1800		
A	www.example.com	SITE IP	1800
CNAME	*.example.com	pixie.porkbun.com 1800

Or just add A record:
Host project.example.com and answer http://example.com:8501 (or http://SITE IP:8501) ? Thanks

You could add another A record

A	project.example.com	SITE IP	1800

and optionally remove the CNAME.


You could also just edit the CNAME like so:

CNAME	*.example.com	example.com 1800

and optionally remove

A	www.example.com	SITE IP	1800
2 Likes

Thanks a lot! I add A record, modify CNAME and delete A record www.example.com, please, write if I did something not optimal.

Can I quickly ask about your tip:

PS: Consider using docker run --restart=always -d -p 127.0.0.1:8501:8501 streamlit, which binds your post :8501 to localhost only, so your service is only accessible via Caddy using your domain configured.

Localhost (127.0.0.1) not working, but work SITE IP (or actually VPS IP) like:

docker run --restart=always -d -p SITE IP:8501:8501 streamlit

Is it okay to use?

My main aim is to increase security and performance. Or maybe some another way of how to do this using Caddyfile. Thanks!

You can also safely delete the project.example.com A record.
Only your top level A record (example.com) and the wildcard (*.example.com) CNAME pointing to example.com is enough.
But it really doesn’t matter all that much.
It will work either way.


You didn’t provide any errors to work with.
I have no idea what you mean by “not working”.

1 Like

Hello, thanks.
There is no errors, just do not access page when running as:
docker run --restart=always -d -p 127.0.0.1:8501:8501 streamlit

Works:

docker run --restart=always -d -p 8501:8501 streamlit
docker run --restart=always -d -p vps ip:8501:8501 streamlit

Is it any difference between vps ip and localhost ip in this case for security and performance? Or what is preferred?

Could it be that you are still using

project.example.com {
        reverse_proxy http://SITE IP:8501
}

instead of

project.example.com {
        reverse_proxy localhost:8501
}

Hello, checked, no

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