Everything was running fine until I switched the application to use https. All the links that the url_for
function generates in the templates now look like this https://ibb.co/N3cJ9V4
Problem:
Mixed Content: The page at 'https://team-mate.app/' was loaded over HTTPS, but requested an insecure stylesheet 'http://team-mate.app/static/css/materialize.min.css'. This request has been blocked; the content must be served over HTTPS.
I've seen similar problems on stackoverflow and tried every possible option, but nothing worked.
I tried the advice I found and ran Uvicorn
through --proxy-headers
Dockerfile ....
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--proxy-headers"]
docker-compose.yaml
....
command: sh -c "alembic upgrade head && uvicorn main:app --host 0.0.0.0 --port 8000 --reload --proxy-headers"
But nothing has changed. The problem remains. Maybe I misunderstood the advice or the nginx config needs to be edited somehow.
The second way I used
from fastapi.middleware.httpsredirect import HTTPSRedirectMiddleware
app = FastAPI()
app.add_middleware(HTTPSRedirectMiddleware)
But got cyclic redirect HTTP/1.0" 307 Temporary Redirect
for all my urls`
Also tried this No effect
My current configs
nginx-config
server {
listen 80;
server_name team-mate.app;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
server_name team-mate.app;
server_tokens off;
location /static/ {
gzip on;
gzip_buffers 8 256k;
root /app;
}
ssl_certificate /etc/letsencrypt/live/team-mate.app/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/team-mate.app/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
location / {
proxy_pass http://web:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /favicon.ico {
access_log off;
log_not_found off;
}
}
docker-compose.yaml
version: '3.9'
services:
web:
env_file: .env
build: .
command: sh -c "alembic upgrade head && uvicorn main:app --host 0.0.0.0 --port 8000 --reload"
volumes:
- .:/app
ports:
- 8000:8000
depends_on:
- db
- redis
db:
image: postgres:11
volumes:
- postgres_data:/var/lib/postgresql/data
expose:
- 5432
environment:
- POSTGRES_USER=${DB_USER}
- POSTGRES_PASSWORD=${DB_PASS}
- POSTGRES_DB=${DB_NAME}
redis:
image: redis:6-alpine
volumes:
- redis_data:/data
nginx:
image: nginx:latest
ports:
- "80:80"
- "443:443"
command: "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"
volumes:
- ./nginx_config.conf:/etc/nginx/conf.d/default.conf
- ./data/nginx:/etc/nginx/conf.d
- ./data/certbot/conf:/etc/letsencrypt
- ./data/certbot/www:/var/www/certbot
depends_on:
- web
certbot:
image: certbot/certbot
volumes:
- ./data/certbot/conf:/etc/letsencrypt
- ./data/certbot/www:/var/www/certbot
entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"
volumes:
postgres_data:
redis_data:
main.py
app = FastAPI()
app.mount("/static", StaticFiles(directory="static"), name="static")
templates = Jinja2Templates(directory="templates")
views.py
@router.get('/', response_class=HTMLResponse)
async def main_page(request: Request,
user: User = Depends(UserService.get_authenticated_user_id)
):
return templates.TemplateResponse('base.html',
context={
'request': request,
'user': user,
}
)
html
<link type="text/css" href="" rel="stylesheet">
<link type="text/css" href="" rel="stylesheet">
p.s.
I followed MatsLindh's advice and added the proxy_set_header X-Forwarded-Proto $scheme
parameter to the nginx configuration
And added --forwarded-allow-ips="*"
to docker-compose file as
command: sh -c "alembic upgrade head && uvicorn main:app --host 0.0.0.0 --port 8000 --reload --proxy-headers --forwarded-allow-ips="*""
And it made a bit of a difference - the debugger stopped writing Mixed-content
but returns 404
error for static files
upd2
In the end, after many attempts to solve the problem, I managed to convert the application to work with https. Now the function url_for
e.g. href=""
is correctly executed and redirects to the correct URL. Also, the warning in the browser that the connection is not secure is gone.
The trick was to use --proxy-headers --forwarded-allow-ips
inside docker-compose
command: sh -c "alembic upgrade head && uvicorn main:app --host 0.0.0.0 --port 8000 --reload --proxy-headers --forwarded-allow-ips="*""
and X-Forwarded-Proto in nginx config
proxy_set_header X-Forwarded-Proto $scheme;
There is one problem left to solve. I still get 404 for the static folder. Therefore, the styles don't work.
My current nginx-config
server {
listen 80;
server_name team-mate.app;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
server_name team-mate.app;
server_tokens off;
location /static/ {
gzip on;
gzip_buffers 8 256k;
alias /app/static/;
}
ssl_certificate /etc/letsencrypt/live/team-mate.app/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/team-mate.app/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
location / {
proxy_pass http://web:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /favicon.ico {
access_log off;
log_not_found off;
}
}
and docker-compose.yaml
version: '3.9'
services:
web:
env_file: .env
build: .
command: sh -c "alembic upgrade head && uvicorn main:app --host 0.0.0.0 --port 8000 --reload --proxy-headers --forwarded-allow-ips="*""
volumes:
- .:/app
ports:
- 8000:8000
depends_on:
- db
- redis
db:
image: postgres:11
volumes:
- postgres_data:/var/lib/postgresql/data
expose:
- 5432
environment:
- POSTGRES_USER=${DB_USER}
- POSTGRES_PASSWORD=${DB_PASS}
- POSTGRES_DB=${DB_NAME}
redis:
image: redis:6-alpine
volumes:
- redis_data:/data
nginx:
image: nginx:latest
ports:
- "80:80"
- "443:443"
command: "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"
volumes:
- ./nginx_config.conf:/etc/nginx/conf.d/default.conf
- ./data/nginx:/etc/nginx/conf.d
- ./data/certbot/conf:/etc/letsencrypt
- ./data/certbot/www:/var/www/certbot
depends_on:
- web
certbot:
image: certbot/certbot
volumes:
- ./data/certbot/conf:/etc/letsencrypt
- ./data/certbot/www:/var/www/certbot
entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"
volumes:
postgres_data:
redis_data:
The interesting thing is that even if I explicitly specify the path to the statics in the templates
<link rel="stylesheet" href="https://team-mate.app/static/css/custom.css'">
it still returns a 404.
from fastapi: url_for in Jinja2 template do not work with https
No comments:
Post a Comment