flutternginx

Path based routing in flutter web with nginx


I want to migrate my flutter web app from Hash to Path-based routing using

usePathUrlStrategy();

My Flutter app uses GoRouter and is deployed as Docker container with nginx.

During local debug mode everything works fine.

But when I deploy it with nginx in a docker container I found that nested subroutes are not served correctly.

So while the routes

https://mywebsite.com/
https://mywebsite.com/tos
https://mywebsite.com/privacy

all work, nested routes like

https://mywebsite.com/deeplink/recipes/asdas

will not work. I still get a 200 though. I think that is because the index.html is loaded instead of the flutter js.

My guess is I did something wrong with my nginx config but I could not find out how to fix the problem. The exact same configuration works with Hash based routing for ALL routes as soon as I remove

usePathUrlStrategy();

This is my GoRouter

final GoRouter _goRouter = GoRouter(routes: [
    GoRoute(
    path: '/',
    builder: (context, state) {
        return const MyHomePage();
    },
    ),
    GoRoute(
        path: "/tos",
        builder: (context, state) => const TermsOfServicePage()),
    GoRoute(
        path: "/privacy",
        builder: (context, state) => const PrivacyPolicyPage()),
    GoRoute(
    path: "/deeplink/recipes/:recipeId",
    builder: (context, state) {
        final String? recipeId = state.pathParameters['recipeId'];
        final String? language = state.uri.queryParameters['lang'];
        return RecipeSharedPage(
        recipeId: recipeId,
        langCode: language,
        );
    },
    ),
]);

And this is my nginx.config

server {
    listen 80;
    server_name mywebsite.com www.mywebsite.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name mywebsite.com www.mywebsite.com;

    ssl_certificate /etc/letsencrypt/live/mywebsite.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/mywebsite.com/privkey.pem;

    add_header Strict-Transport-Security "max-age=63072000; preload" always;

    location / {
        root /usr/share/nginx/html;
        index index.html;
        try_files $uri $uri/ /index.html;
    }

    location /assets/ {
        root /usr/share/nginx/html;
        try_files $uri $uri/ =404;
    }
}

I am getting the following errors in the browser console which seem to indicate that the index.html was loaded when js was expected

Exception while loading service worker: DOMException: 
Failed to register a ServiceWorker for scope ('https://mywebsite.com/deeplink/recipes/') 
with script ('https://mywebsite.com/deeplink/recipes/flutter_service_worker.js?v=3781291051'): 
The script has an unsupported MIME type ('text/html').

The script has an unsupported MIME type ('text/html').

Uncaught SyntaxError: Unexpected token '<' (at main.dart.js:1:1)

Do you know what the reason is?


Solution

  • I found the reason for this. In my Dockerfile I was pulling the latest stable flutter release for building the app which at the moment was 3.24.

    However, in Flutter 3.24 (for both a flutter run and flutter build run apps for web) this issue in flutter caused the problem of not working nested links in flutter web in case path based routing was enabled.

    I did not notice it in local sessions with flutter run on my dev PC since my flutter version was lower.

    I resolved the error for now by using a fixed lower version for flutter in my Dockerfile.