Why does /studio/desk give 404 on refresh but /studio redirects correctly?

47 replies
Last updated: Dec 29, 2022
If I refresh website at /studio/desk I get a 404, but if a refresh to /studio things will reroute to /studio/desk but things work.
Can someone please explain why that might be happening?
AI Update

This is a common issue when hosting Sanity Studio as a single-page application (SPA) under a subpath like /studio. The problem is related to how your web server handles direct requests to nested Studio routes.

Why This Happens

When you navigate to /studio and then click through to /studio/desk, the Studio's client-side router handles the navigation smoothly. However, when you refresh the page at /studio/desk, your web server receives a direct HTTP request for that path. Since Sanity Studio is a SPA, all routes under /studio/* need to be served by the same index.html file so the client-side router can take over. Without proper server configuration, the server looks for a file at /studio/desk, doesn't find one, and returns a 404.

The Solution

You need to configure your hosting platform to treat all requests under /studio/* as SPA routes. The exact configuration depends on your hosting platform:

For Vercel, create or update vercel.json in your project root:

{
  "rewrites": [
    { 
      "source": "/studio/:path*", 
      "destination": "/studio/index.html" 
    }
  ]
}

For Netlify, add to your netlify.toml or _redirects file:

[[redirects]]
  from = "/studio/*"
  to = "/studio/index.html"
  status = 200

For Apache, use .htaccess:

<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteBase /
  RewriteRule ^studio/index\.html$ - [L]
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteRule ^studio/ /studio/index.html [L]
</IfModule>

For Nginx:

location /studio {
  try_files $uri $uri/ /studio/index.html;
}

Important Configuration Note

If you're embedding Studio at a subpath like /studio, make sure your Studio CLI configuration is properly set up. In modern Sanity Studio (v3+), you configure the base path in sanity.cli.js or sanity.cli.ts:

export default {
  api: {
    projectId: 'your-project-id',
    dataset: 'production'
  },
  basePath: '/studio'
}

This tells the Sanity CLI where to build and serve the files during development and ensures the correct paths are used in production when you run sanity build.

What to Avoid

Don't use a catch-all rewrite that redirects all routes to a single index file, as this will break any other routes in your application (like your Next.js pages, API routes, or static assets).

The key is ensuring that only requests under /studio/* are rewritten to the Studio's entry point, allowing the rest of your application to function normally.

Show original thread
47 replies
could you share your sanity.config and your vercel.config, if you deployed it there?
Sanity Config…
{
  "root": true,
  "project": {
    "name": "Karrie's Studio",
    "basePath": "/studio"
  },
  "api": {
    "projectId": "...",
    "dataset": "production"
  },
  "plugins": [
    "@sanity/base",
    "@sanity/components",
    "@sanity/default-layout",
    "@sanity/default-login",
    "@sanity/desk-tool",
    "asset-source-unsplash"
  ],
  "env": {
    "development": {
      "plugins": ["@sanity/vision"]
    }
  },
  "parts": [
    {
      "name": "part:@sanity/base/schema",
      "path": "./schemas/schema.js"
    },
    {
      "implements": "part:@sanity/base/brand-logo",
      "path": "./logo/myLogo.js"
    },
    {
      "implements": "part:@sanity/base/theme/variables/override-style",
      "path": "./styles/variables.css"
    }
  ]
}
I don’t know how to share the Vercel config
Which version of sanity are you using?
Regardless there should be a
vercel.config.ts
file in the root directory of the studio 🙂 (when you deploy the studio to
YOUR_DOMAIN/studio
which it appears you do)
Interesting
This is my package json file:
{
	"name": "mysanityproject",
	"private": true,
	"version": "1.0.0",
	"description": "",
	"main": "package.json",
	"author": "Knut Melvær <knut@sanity.io>",
	"license": "UNLICENSED",
	"keywords": [
		"sanity"
	],
	"dependencies": {
		"@sanity/base": "^2.0.5",
		"@sanity/components": "^2.0.5",
		"@sanity/core": "^2.0.5",
		"@sanity/default-layout": "^2.0.5",
		"@sanity/default-login": "^2.0.5",
		"@sanity/desk-tool": "^2.0.5",
		"@sanity/vision": "^2.0.5",
		"prop-types": "^15.6",
		"react": "^16.2",
		"react-dom": "^16.2",
		"react-hook-form": "^6.7.0",
		"sanity-plugin-asset-source-unsplash": "^0.1.3"
	}
}
I have another project deployed that is similar (same version and mostly same setup from what I gather), but it doesn’t have the problem of 404ing when I go to /studio/desk like this one has.
okay so an old version of v2, but it should work. You need to check the vercel deployment setup, seems like something is weird there 🙂 That would be my guess
Hmm okay
I was thinking to check the ENV Variables, but would you suggest what else I should look into?
(On Vercel)
I’ve made a screen recording of this issue.
This is on Chrome, in incognito mode.
So, ⬆️ above is what it looks like for the client.
I am going to share another screen recording of what it looks like for me in my chrome web browser (not in incognito mode)

My browser ⬇️ (successful attempt)
Seems like you aren't being logged in properly.
It also seems like something super specific to this website though. Other sites don’t behave like this with the need to manually remove
/desk
from the URL
Did you configure cors properly?
I was wondering about that too
It seems that it is setup for this site correctly
I may just try to upgrade sanity and see if that will have any positive effect
Just for kicks remove the https and replace with http in the cors setup.
Yea I got this error (Even if I try to access http):
Ah well that isn't it lol.
Have you tried it in another browser?
Yea - With Safari it just doesn’t work. Same problem with Firefox
Ok yea get the simple things out of the way first. Hmmmm
https://www.sanity.io/docs/example-migrating-the-blog-template-from-studio-v2-to-v3
Looks like this may be your best bet. It looks quite intensive. I couldn't even find the docs for v2.
Thanks! You’ve been very helpful so far!
Have you tried deploying to a .sanity.studio url? Just to see if it works there.
Yea and that works fine
Then: you have not deployed it on your site… Could you find the
vercel.config
or
next.config
OR
jsconfig.json
?
This is my
next.config
:
module.exports = {
  images: {
    domains: ['<http://cdn.sanity.io|cdn.sanity.io>', '<http://images.unsplash.com|images.unsplash.com>'],
  },
}
This is my
jsconfig.json
:
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/components/*": ["components/*"],
      "@/lib/*": ["lib/*"],
      "@/context/*": ["context/*"],
      "@/hooks/*": ["hooks/*"],
    }
  }
}
Not sure how to access vercel.config though
I haven’t started the upgrade process
user J
- I will just wait to see what you have to say, but would you suggest going through and doing that?
And maybe the issue is that I don’t have a vercel.config file in the root of my studio directory
Wait never mind the above - My other client’s site doesn’t have this either, but it is working
Oh wait - I have a vercel.json file in the root of my Next.js directory that looks very different from the other project.Here is the current project (broken version):

// vercel.json
{
  "version": 2,
  "rewrites": [{ "source": "/(.*)", "destination": "/index.html" }]
}
Versus the working version:

// vercel.json
{
    "rewrites": [{ "source": "/studio/(.*)", "destination": "/studio/index.html" }]
}
That worked btw!
Nice!
Looks like it was looking to the root which was causing a conflict.
Yea that makes sense! I figured it would be something super simple haha
Wonderful 🙂 Sometimes a small thing can cause big issues
With all the different version flying around one of the files I mentioned will be the culprits 😉 And it was
🤝

Sanity – Build the way you think, not the way your CMS thinks

Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.

Was this answer helpful?