Skip to content

Fresh 2.x basePath: Root Route Not Mapped Correctly #3618

Description

@jackbrumley

Fresh 2.x basePath: Root Route Not Mapped Correctly

Summary

Hi all, i'm running into an issue where App({ basePath }) in Fresh 2.x doesn't correctly map the root route (routes/index.tsx) to the basePath location. Everything else works - nested routes, API routes, static assets - but hitting the root path returns a 404.

Environment

  • Fresh version: jsr:@fresh/core@^2.1.4
  • Deno version: 2.5.6 (stable, release, x86_64-unknown-linux-gnu)
  • V8: 14.0.365.5-rusty
  • TypeScript: 5.9.2
  • OS: Linux (Debian)
  • Deployment: nginx reverse proxy forwarding to Fresh app

Use Case

Running Fresh application behind nginx reverse proxy at a custom subpath for multi-app deployment:

  • nginx proxy: https://example.com/apps/myapp/http://10.64.2.211:8081/apps/myapp/
  • nginx preserves full path (no rewrite)
  • Fresh configured with basePath: "/apps/myapp"

Expected Behavior

With basePath: "/apps/myapp" set, all routes should be accessible under the basePath prefix:

  • GET /apps/myapp/ → should route to routes/index.tsx
  • GET /apps/myapp/d/123/doc → should route to routes/d/[uuid]/[[slug]].tsx
  • GET /apps/myapp/api/spaces → should route to routes/api/spaces.ts

Actual Behavior

With basePath: "/apps/myapp" set:

  • GET /apps/myapp/404 - Route not found (FAILS)
  • GET /apps/myapp/d/123/doc → works correctly
  • GET /apps/myapp/api/spaces → works correctly

Only the root route fails; all nested routes work as expected.

Reproduction

1. Fresh App Configuration

main.ts:

import { App, staticFiles } from "fresh";

const basePath = Deno.env.get("BASE_PATH") || "";

export const app = new App<State>({
  basePath,
});

app.use(staticFiles());
app.fsRoutes();

routes/index.tsx:

import { define } from "../utils.ts";

export const handler = define.handlers({
  async GET(ctx) {
    // Route logic here
    return { data: { message: "Home page" } };
  },
});

export default define.page(function Home(props) {
  return <div>Welcome</div>;
});

Environment:

BASE_PATH="/apps/myapp"
SERVER_PORT="8081"

2. nginx Configuration

Setup:

  • Domain dev.docillo.com configured with Custom Location
  • Location /apps/docs/ forwarded to Fresh app at http://10.64.2.211:8081

Proxy configuration:

# Custom Location: /apps/docs/
location /apps/docs/ {
    proxy_pass http://10.64.2.211:8081;
    
    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;
}

Key points:

  • No path stripping/rewriting in nginx
  • nginx forwards /apps/docs/ as-is to Fresh app running on 10.64.2.211:8081
  • Fresh receives the full path including basePath

3. Steps to Reproduce

  1. Set up Fresh app with basePath configuration as shown above
  2. Create routes/index.tsx with a simple handler
  3. Create nested route like routes/d/[uuid]/[[slug]].tsx
  4. Configure nginx to forward /apps/myapp/* to Fresh app
  5. Start Fresh app with BASE_PATH="/apps/myapp"
  6. Access routes:
    • https://example.com/apps/myapp/404
    • https://example.com/apps/myapp/d/test-uuid/docworks

Evidence

Server Logs

Fresh startup confirms basePath is configured:

🚀 SERVER STARTING
Version: 0.2.0
Environment: development
Port: 8081

[Bootstrap] Routes mounted with basePath: /apps/myapp
[App] Fresh app v0.2.0 platform ready

Request to root returns 404:

[Middleware] GET /apps/myapp/
[Middleware] 404 - Route not found: /apps/myapp/

Nested routes work correctly:

[Middleware] GET /apps/myapp/d/a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d/doc
[Middleware] Route access: /apps/myapp/d/a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d/doc

Middleware Investigation

Middleware confirms route matching failure for basePath root:

export default define.middleware(async (ctx) => {
  const url = new URL(ctx.req.url);
  const pathname = url.pathname;
  
  if (ctx.route === null) {
    debug(`404 - Route not found: ${pathname}`);
    return await ctx.next();
  }
  
  debug(`Route access: ${pathname}`);
  return await ctx.next();
});

Result:

  • /apps/myapp/ctx.route === null (FAILS)
  • /apps/myapp/d/...ctx.route !== null (works)
  • /apps/myapp/api/...ctx.route !== null (works)

Analysis

The basePath configuration is successfully applied to:

  • Static assets (served under /apps/myapp/assets/*)
  • Nested file routes (routes/d/[uuid]/.../apps/myapp/d/...)
  • API routes (routes/api/.../apps/myapp/api/...)

But fails for:

  • Root route (routes/index.tsx/apps/myapp/)

This suggests that App({ basePath }) correctly prefixes nested routes discovered by app.fsRoutes(), but the special case of the root route (index.tsx → "/") is not being mapped to basePath + "/".

Impact

This issue prevents Fresh applications from being deployed at custom subpaths via reverse proxy when the root route needs to be accessible. It forces developers to either:

  1. Not use custom basePaths
  2. Implement manual route workarounds
  3. Deploy each Fresh app at its own domain

Additional Context

  • Direct access without basePath works: http://localhost:8081/ routes correctly to routes/index.tsx
  • The issue only manifests when basePath is non-empty
  • Assets and nested routes respect basePath correctly
  • Trailing slash behavior confirmed: both /apps/myapp and /apps/myapp/ return 404

Related

Similar issues may have been reported regarding basePath support in Fresh 2.x. This appears to be a gap in how the root route is registered when basePath is set.


Expected Fix:
When App({ basePath: "/apps/myapp" }) is configured and app.fsRoutes() is called, the root route from routes/index.tsx should be mapped to /apps/myapp/ just as nested routes are correctly mapped under the basePath prefix.


Just wanted to say thanks for all the work on Fresh. This is my first web framework and I'm really enjoying working with it - building some cool stuff for myself and my workplace. Despite running into this issue, the overall experience has been great. Keep up the excellent work!

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions