Open jan-thoma opened 2 years ago
This seems like it would be addressed by web server. Can you provide some more info about your reverse proxy/web sever configuration?
Without placing craft in that subdirectory
when you say "placing craft", do you mean just index.php
?
It's docker setup with traefik in front, with Craft, Vue and MySQL running in their separate containers. Traefik routes everything except urls starting with `/backend to the Vue container.
Urls starting with /backend
will be routed to the craft container, traefik strips /backend
part. The @web
path is set to e.g. my.domain.com/backend
.
What's working:
What's not working:
@jan-thoma are you able to share anything where I could reproduce the behavior (eg dockerfiles/docker-compose)?
version: '3.2'
services:
viiv-podcast-node:
image: registry.io.t-k-f.ch/viiv/viiv-podcast/viiv-podcast-frontend:latest
restart: always
networks:
- external
depends_on:
- viiv-podcast-craft
labels:
- "traefik.enable=true"
- "traefik.docker.network=${TRAEFIK_NETWORK}"
- "traefik.http.routers.${TRAEFIK_NAME_FRONTEND}.rule=Host(${TRAEFIK_HOST})"
- "traefik.http.routers.${TRAEFIK_NAME_FRONTEND}.entrypoints=websecure"
- "traefik.http.routers.${TRAEFIK_NAME_FRONTEND}.tls.certresolver=letsencrypt-tls"
- "traefik.http.routers.${TRAEFIK_NAME_FRONTEND}.middlewares=${TRAEFIK_NAME_FRONTEND}-www-to-non-www"
- "traefik.http.middlewares.${TRAEFIK_NAME_FRONTEND}-www-to-non-www.redirectregex.regex=^https://www.(.*)"
- "traefik.http.middlewares.${TRAEFIK_NAME_FRONTEND}-www-to-non-www.redirectregex.replacement=https://$${1}"
- "traefik.http.middlewares.${TRAEFIK_NAME_FRONTEND}-www-to-non-www.redirectregex.permanent=true"
- "traefik.http.services.${TRAEFIK_NAME_FRONTEND}.loadbalancer.server.port=${TRAEFIK_PORT_FRONTEND}"
environment:
- VITE_APP_BACKEND_FQDN=${BACKEND_URL}
- VITE_APP_CACHE_PAGES=${VITE_APP_CACHE_PAGES}
- VITE_APP_CACHE_AGE=${VITE_APP_CACHE_AGE}
viiv-podcast-craft:
image: registry.io.t-k-f.ch/viiv/viiv-podcast/viiv-podcast-backend:latest
restart: always
hostname: fuckyou
networks:
- external
- internal
volumes:
- backend:/app/web/data
depends_on:
- viiv-podcast-mysql
labels:
- "traefik.enable=true"
- "traefik.docker.network=${TRAEFIK_NETWORK}"
- "traefik.http.routers.${TRAEFIK_NAME_BACKEND}.rule=Host(${TRAEFIK_HOST}) && PathPrefix(`/backend`)"
- "traefik.http.routers.${TRAEFIK_NAME_BACKEND}.entrypoints=websecure"
- "traefik.http.routers.${TRAEFIK_NAME_BACKEND}.tls.certresolver=letsencrypt-tls"
- "traefik.http.routers.${TRAEFIK_NAME_BACKEND}.middlewares=${TRAEFIK_NAME_BACKEND}-www-to-non-www"
- "traefik.http.routers.${TRAEFIK_NAME_FRONTEND}.middlewares=${TRAEFIK_NAME_FRONTEND}-stripprefix"
- "traefik.http.middlewares.${TRAEFIK_NAME_BACKEND}-www-to-non-www.redirectregex.regex=^https://www.(.*)"
- "traefik.http.middlewares.${TRAEFIK_NAME_BACKEND}-www-to-non-www.redirectregex.replacement=https://$${1}"
- "traefik.http.middlewares.${TRAEFIK_NAME_BACKEND}-www-to-non-www.redirectregex.permanent=true"
- "traefik.http.middlewares.${TRAEFIK_NAME_BACKEND}-stripprefix.stripprefix.prefixes=/backend"
environment:
- ENVIRONMENT=${ENVIRONMENT}
- APP_ID=${APP_ID}
- DB_DRIVER=${DB_DRIVER}
- DB_SERVER=${DB_SERVER}
- DB_PORT=${DB_PORT}
- DB_DATABASE=${DB_DATABASE}
- DB_USER=${DB_USER}
- DB_PASSWORD=${DB_PASSWORD}
- DB_SCHEMA=${DB_SCHEMA}
- DB_TABLE_PREFIX=${DB_TABLE_PREFIX}
- FROM_EMAIL=${FROM_EMAIL}
- FROM_NAME=${FROM_NAME}
- REPLY_TO_EMAIL=${REPLY_TO_EMAIL}
- SMTP_HOST=${SMTP_HOST}
- SMTP_PORT=${SMTP_PORT}
- TO_EMAIL=${TO_EMAIL}
- TO_SUBJECT=${TO_SUBJECT}
- SECURITY_KEY=${SECURITY_KEY}
- DEV_MODE=${DEV_MODE}
- ALLOW_ADMIN_CHANGES=${ALLOW_ADMIN_CHANGES}
- DISALLOW_ROBOTS=${DISALLOW_ROBOTS}
- TIMEZONE=${TIMEZONE}
- BACKEND=${BACKEND_URL}
- FRONTEND=${FRONTEND_URL}
- WEBROOT=${WEBROOT}
viiv-podcast-mysql:
image: mysql
restart: always
networks:
- internal
volumes:
- database:/var/lib/mysql
environment:
- MYSQL_RANDOM_ROOT_PASSWORD=${DB_PASSWORD}
- MYSQL_DATABASE=${DB_DATABASE}
- MYSQL_USER=${DB_USER}
- MYSQL_PASSWORD=${DB_PASSWORD}
viiv-podcast-smtp:
image: ixdotai/smtp
restart: always
hostname: ${HOSTNAME}
networks:
- internal
volumes:
- ./_docker_additional_macros:/etc/exim4/_docker_additional_macros
networks:
external:
external: true
internal:
driver: bridge
volumes:
database:
backend:
@jan-thoma perfect, thanks! I'll dig into this asap.
@jan-thoma thank you for your patience.
Some follow-up questions so I can reproduce what you're seeing:
viiv-podcast-craft
image? Is it php-fpm+nginx? Is it using https://github.com/craftcms/docker#nginx as a base?Here's my Dockerfile to build the image
FROM composer:2 as vendor
COPY composer.json composer.json
COPY composer.lock composer.lock
RUN composer install --ignore-platform-reqs --no-interaction --prefer-dist
FROM craftcms/nginx:7.4
WORKDIR /app
COPY --chown=www-data:www-data --from=vendor /app/vendor/ /app/vendor/
COPY --chown=www-data:www-data . .
RUN chmod +x bootstrap.sh
RUN mkdir storage
ENTRYPOINT ["./bootstrap.sh"]
And my Traefik config
version: '3'
services:
reverse-proxy:
image: traefik:v2.4
restart: always
command:
- "--accessLog=true"
- "--api.dashboard=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.ssh.address=:22"
- "--entrypoints.web.address=:80"
- "--entrypoints.web.http.redirections.entrypoint.to=websecure"
- "--entrypoints.web.http.redirections.entrypoint.scheme=https"
- "--entrypoints.websecure.address=:443"
- "--entrypoints.tcp1883.address=:1883"
- "--entrypoints.tcp3306.address=:3306"
- "--entrypoints.tcp6524.address=:6524"
- "--entrypoints.tcp6525.address=:6525"
- "--entrypoints.tcp6526.address=:6526"
- "--certificatesresolvers.letsencrypt-tls.acme.tlschallenge=true"
- "--certificatesresolvers.letsencrypt-tls.acme.email=example@example.org"
- "--certificatesresolvers.letsencrypt-tls.acme.storage=/letsencrypt/acme.json"
networks:
- external
ports:
- "22:22"
- "80:80"
- "443:443"
- "6524:6524"
- "6525:6525"
- "6526:6526"
volumes:
- traefik-letsencrypt:/letsencrypt
- /var/run/docker.sock:/var/run/docker.sock
labels:
- "traefik.enable=true"
- "traefik.docker.network=${TRAEFIK_NETWORK}"
- "traefik.http.routers.${TRAEFIK_NAME}.rule=Host(${TRAEFIK_HOST})"
- "traefik.http.routers.${TRAEFIK_NAME}.service=api@internal"
- "traefik.http.routers.${TRAEFIK_NAME}.entrypoints=websecure"
- "traefik.http.routers.${TRAEFIK_NAME}.tls.certresolver=letsencrypt-tls"
- "traefik.http.routers.${TRAEFIK_NAME}.middlewares=${TRAEFIK_NAME}-www-to-non-www"
- "traefik.http.routers.${TRAEFIK_NAME}.middlewares=${TRAEFIK_NAME}-auth"
- "traefik.http.middlewares.${TRAEFIK_NAME}-www-to-non-www.redirectregex.regex=^https://www.(.*)"
- "traefik.http.middlewares.${TRAEFIK_NAME}-www-to-non-www.redirectregex.replacement=https://$${1}"
- "traefik.http.middlewares.${TRAEFIK_NAME}-www-to-non-www.redirectregex.permanent=true"
- "traefik.http.services.${TRAEFIK_NAME}.loadbalancer.server.port=${TRAEFIK_PORT}"
networks:
external:
external: true
volumes:
traefik-letsencrypt:
@jan-thoma can you try setting 'baseCpUrl' => 'my.domain.com/backend',
(the same value you're setting the @web
alias to in config/general.php
?
WEB=https://viiv.podcast.nitro/backend
WEBROOT=/app/web
BASE_CP_URL='https://viiv.podcast.nitro/backend'
With this settings the login attempt still hits an error 404
@jan-thoma thanks again for your patience! I've managed to get this all working and reproducible, here: https://github.com/timkelty/craftcms-subpath-proxy
It is a default Craft install, running with docker-compose, and a stripped down version of your Traefik rules (just the subpath stuff).
There a number of important settings required, and a bugfix in Craft. Please see the README.
Let me know when you get a chance to test!
@timkelty
If i run your example assets won't get served correctly. I allready found the culprit in the labels section of the docker-compose file. There was a typo in my labels section, which caused the stripprefix
middleware to not working properly. it should look like this, otherwise the stripprefix middleware is not working and gets skipped.
labels:
- "traefik.enable=true"
- "traefik.http.routers.${TRAEFIK_NAME_BACKEND}.rule=Host(`${TRAEFIK_HOST}`) && PathPrefix(`/backend`)"
- "traefik.http.routers.${TRAEFIK_NAME_BACKEND}.entrypoints=web"
- "traefik.http.routers.${TRAEFIK_NAME_BACKEND}.middlewares=${TRAEFIK_NAME_BACKEND}-stripprefix"
- "traefik.http.middlewares.${TRAEFIK_NAME_BACKEND}-stripprefix.stripprefix.prefixes=/backend"
This solved the asset issue, but caused a new one. The CP is now reachable under /backend/backend/admin
and tries to redirect all request to /backend/admin
If i run it without the stripprefix
middleware actions are working but CP resources calls hitting a 404
@jan-thoma when you say "assets", are you referring to cp resources? Everything in the CP loads find for me, using my example.
If you go to the login page before installing via cli, they won't load, because craft throws a 503, even for those cp resources.
There was a typo in my labels section
The missing backticks, right? My version has the backticks.
The CP is now reachable under /backend/backend/admin
Hmmm. That's not happening in my example…
Holler if you'd rather hop on a call and hash it out together. GIven that it works in my example (for me), I'm either misunderstanding your aim, or it isn't working for you for some other reason.
@timkelty
Let me try to explain it a little further.
First the traefik config.
At the moment it's
- "traefik.http.routers.${TRAEFIK_NAME_FRONTEND}.middlewares=${TRAEFIK_NAME_FRONTEND}-stripprefix"
It should be
- "traefik.http.routers.${TRAEFIK_NAME_FRONTEND}.middlewares=${TRAEFIK_NAME_BACKEND}-stripprefix"
for the stripprefix middleware to work. This was a typo from my side sorry about that. Anyway we have two options here to run craft on the subpath behind traefik.
Without stripprefix middleware
A request to mysite.com/backend/admin
will be seen from craft's perspective as a request to mysite.com/backend/admin
In this scenario everything works fine, but calls to files in a local Assets Volumes folder will end in a 404. Here's the Volume config
With stripprefix middleware
A request to mysite.com/backend/admin
will be seen from craft's perspective as a request to mysite.com/admin
In this case the CP is at mysite.com/backend/backend/admin
instead of mysite.com/backend/admin
. Links and action in the CP redirect to mysite.com/backend/*
, files in Asset Volumes are loaded correctly.
From a technical standpoint booth, with or without the stripprefix middleware are valid options to go, the question is which configuration would work the best. I hope my explanations made it clearer to get a grasp of the problem i'm facing here. Let's schedule a call if more details are needed.
@timkelty
I found another approach to solve the problem. To get Craft working on the same domain as the frontend there a set of paths which needs to be routed to backend container.
/admin => CP
/cpressources => CP Resources
/actions => Action trigger
/graphql => GraphQL Endpoint (can be any path)
/data => Asset Volume (can be any path)
With an traefik config like this everything seems to work fine
labels:
- "traefik.enable=true"
- "traefik.docker.network=${TRAEFIK_NETWORK}"
- "traefik.http.routers.${TRAEFIK_NAME_BACKEND}.rule=Host(${TRAEFIK_HOST}) && PathPrefix(`/admin`, `/data`, `/graphql`, `/cpresources`, `/actions`)"
- "traefik.http.routers.${TRAEFIK_NAME_BACKEND}.entrypoints=websecure"
- "traefik.http.routers.${TRAEFIK_NAME_BACKEND}.tls.certresolver=letsencrypt-tls"
- "traefik.http.routers.${TRAEFIK_NAME_BACKEND}.middlewares=${TRAEFIK_NAME_BACKEND}-www-to-non-www"
- "traefik.http.middlewares.${TRAEFIK_NAME_BACKEND}-www-to-non-www.redirectregex.regex=^https://www.(.*)"
- "traefik.http.middlewares.${TRAEFIK_NAME_BACKEND}-www-to-non-www.redirectregex.replacement=https://$${1}"
- "traefik.http.middlewares.${TRAEFIK_NAME_BACKEND}-www-to-non-www.redirectregex.permanent=true"
There could be some issues with custom plugin frontend routes but these should be callable with their respective action trigger. Did i missed some routes which are necessary for Craft to work properly?
Did i missed some routes which are necessary for Craft to work properly?
@jan-thoma Nope, those are all the paths! Worth noting that all of those paths are configurable as well.
In this scenario everything works fine, but calls to files in a local Assets Volumes folder will end in a 404. Here's the Volume config
At this point in the request, Craft should have nothing to do with it. They're falling back to a Craft 404 because Nginx is doing try_files
and including the prefix.
One easy option is to add a rule to the nginx config to rewrite those, like I've added to my example.
I'm sure you could do this directly in Traefik as well, I just know know how off-hand.
In this case the CP is at mysite.com/backend/backend/admin
I'm still never seeing this behavior in my example.
–––
I've now updated my example that seems to check all your requirements. Care to give it another try?
backend
foldercpresources
working as expected/backend/assets/*
, serving from /app/web/assets/*
Description
When running craft behind a reverse proxy in this manner:
Calls to index.php always resolve to the root of the domain and therefore fail. Is there a way to solve this problem. Without placing craft in that subdirectory. Background we run Craft and SSR Vue.js Docker Containers along. To avoid CORS Issues we would like to serve the Craft container from a subpath of the domain.
Additional info