serverless-next.js: Catch-all at /pages root breaks static file serving

Describe the bug

Next supports having a catch-all route at pages/[...all] (this works with next start). However, when I add this file (with getStaticPaths fallback: true) and deploy using serverless-next.js, requests for static assets break.

Actual behavior

Assets don’t load

  • in some cases, they 503
  • in other cases, it seems requests for static data are handled by pages/[...all] (next’s fallback rendering). This results in a 200 with type text/html. I can paste these asset URLs in my browser and get the fallback page.

This applies to all kinds of assets (next’s /_next files as well as things in public/).

Expected behavior

Static files are served correctly.

Steps to reproduce

Add a catch-all route at pages/[...all] with getStaticPaths. (I haven’t tested this with SSR)

Versions

Other info

When I add fallback: false the requests become 503s, but otherwise the issue is the same.

Does serverless-next.js support this? I couldn’t find mention in the documentation, sorry if I missed something. It makes sense that this would be tricky, but wondering if there’s a known workaround.

If this is unsupported, is there a roadmap or further discussion on this? This is very high-value to my team, as it would unlock the ability to define any URL path using CMS.


ETA:

  • If it’s helpful, my cloudwatch logs are full of this error image
  • Also, I tried this with a dynamic, non-catch-all route (pages/[all]) and it still breaks for public/test.png, but no longer breaks for public/assets/test.png or for /_next assets. This seems to confirm the rule that assets URLs can’t match /pages patterns.

My solution for now is to both a) use [all] for content pages (ie limit created pages to one path segment), and b) require everything in public to be in public/assets so they don’t match [all]

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 1
  • Comments: 33

Most upvoted comments

Hi, I’m still having this issue. It seems my favicon, which is served from the public folder, loads incorrectly (it loads a URL relative to my catch-all route). The favicon is being loaded in the head of my custom _document.tsx page. As for the catch-all route, it’s just products/[...slug] and is using getStaticProps and getStaticPaths. Any suggestion would be appreciated 😃

Screen Shot 2021-06-20 at 16 12 50

Okay so it’s an error on my part and I have to apologize. The docs have to be followed strictly Screen Shot 2021-06-20 at 19 48 15

The mistake I made was I referenced my favicon which was located in public/assets/favicon.ico by assets/favicon.ico which works for non-catch-all routes but not for catch-all routes. The correct way to write it would be /assets/favicon.ico. I hope whoever sees this finds it useful!

Hey @dphang my bad, was testing out a fix I made to lambda-at-edge, which I deployed to the demo.

https://github.com/himynameistimli/serverless-next.js/blob/6c966fdc6ed0b6909b164126cd58e8d5c20c5799/packages/libs/lambda-at-edge/src/default-handler.ts#L576-L583

It fixes it for my specific use case, but that’s because I don’t make use of fallbacks for SSG pages. I copied the repro manifests into a fiddle to see what was happening. https://jsfiddle.net/sbpxqeft/. hasFallbackForUri will return a different prerendered dynamic route (for an SSG page) if it has a more generalized regex that also happens to to work with a dynamic SSR route.

So I essentially told it to ignore the fallback if there is a dynamic SSR route that also matches. I don’t know if this is generally right, but I think that there should be some check between the manifest and the prerenderManifest to make sure that they are talking about the same route.

I will restore the demo back to the latest beta. Sorry for the confusion.

I see, I added rewrites to the e2e-test next app here:

module.exports = {
  async rewrites() {
    return [
      {
        source: "/:path*",
        destination: "/:path*"
      },
      {
        source: "/:path*",
        destination: "/ssg-page"
      }
    ];
  }
};

When I added the first rewrite, the next dev server is only matching the 1st rewrite for me, hence in that app it goes to a catch all page I defined at the root. If I remove the first one, then any path is rewritten to ssg-page and it’s served correctly - an SSG page. This seemed to be same behavior on both Next.js dev server and serverless-next.js. Although this is Next.js 9.5.5, not sure if something changed with the later versions.

Anyway, so I thought that first no-op rewrite is only needed when you are proxying to another domain, which I hadn’t implemented yet. But the way it’s implemented now, it doesn’t know about this special no-op rewrite; the first rewrite will get matched. I think there are a few things to fix:

  1. We need to make sure we route existing SSR/SSG pages correctly when dynamic routes / catch-all at same level as those is present. I thought I fixed the public files / SSG pages, but SSR pages like you mentioned are broken (will fix it today).
  2. For rewrites, I think there is some mismatch between this implementation and Next.js’s. I will look into fixing the gap.
  3. I’m not sure why public file serving is still breaking. Could you please check CloudWatch logs for any error responses and add them as well (and for the other broken routes, if they are now different?)?

BTW, do you have a minimal repro you can create and share? It will make it easier to debug and fix the specific scenario(s) you have in mind. Since in this case it looks like multiple things (catch-all, public files, SSR/SSG pages, rewrites) are interacting with each other, it may be hard for me to repro just via descriptions.

Thanks!

Thanks, I will take a look at this this week.

I think this component should support it according to features list but I believe catch-all and getStaticPaths were each implemented separately, perhaps there’s a bug when both are used.