[FIXED] Flask redirect url_for generates Cloud Run service URL instead of domain

Issue

Background:
I’ve built and deployed an app with Google Cloud Firebase. At a high level, I have a Python Flask server running on Cloud Run, and I serve static JS files with Firebase hosting.

Issue:
Sometimes, I want to redirect the user, but I’m sending them to the Cloud Run service URL rather than my app domain.

EDIT: I’m NOT experiencing this in the JS on the browser, but ONLY in the Python on the server.

Python

If a user navigates to a page without being signed in, e.g. following a link, they are redirected to my login page. For example, if someone who is not signed in tries to look at someone else’s profile, the following code redirects them to the authentication blueprint’s login endpoint:

if not session.get('user'):
    return redirect(url_for('authentication.login'))

I would expect them to be redirected to my-app-name.web.app/auth/login but instead they’re routed to my-cloudrun-service-name-XXXXX-region.run.app/auth/login. While the pages loaded look the same, they’re really not. The second one redirects to the Cloud Run service URL, which doesn’t have my static files served by Firebase Hosting.

I’m not really sure, but I believe this happens because Flask is generating the URL using the request context. So the request from the browser hits Cloud Run Load Balancer, which directs the request to my Cloud Run instance, but that means the Flask app in my Cloud Run instance only sees the internal Google Cloud redirect, and it doesn’t know my domain.

I’ve tried solving this by setting app.config[‘SEVER_NAME’] = my-app-name.web.app, but I just get the "Not Found" page on each request. Is SEVER_NAME the solution but I’m not implementing it correctly, or is there another way to fix the Flask url_for generation?

Solution

I’ve found what I deem to be an effective solution / workaround.

I set the app config to store my BASE_URL when the app is created:

app.config['BASE_URL'] = 'https://my-url.domain'

Then I can access this as application context during requests, even from blueprints:

@blueprint.route('my_route/')
def my_route():
  if not session.get('user'):
    return redirect(current_app.config['BASE_URL'] + url_for('authentication.login', 302)

This has two key benefits for me:

  1. There’s only one place, the app config, to update if I change the domain
  2. url_for is still used, so there’s no hardcoding in case blueprints or routes change

Answered By – Brandon Fuerst

Answer Checked By – Laura B. (Easybugfix Admin)

Leave a Reply

(*) Required, Your email will not be published