Skip to content

Don't let browsers cache 301 responses forever by default #59346

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
janopae opened this issue Jan 2, 2025 · 8 comments
Open

Don't let browsers cache 301 responses forever by default #59346

janopae opened this issue Jan 2, 2025 · 8 comments

Comments

@janopae
Copy link

janopae commented Jan 2, 2025

Description

Permanent caching can be very "dangerous" in the way that you might introduce a problem that can never be resolved, because clients never revalidate the cache.

Sadly, by default, most browsers cache 301 redirects forever, unless explicit Cache-Control headers are set: https://makersaid.com/browser-behavior-301-redirects/

As there is always the possibility of sending an incorrect response and needing to correct it, it is always more safe to disable caching or limit it to a certain time frame.

At webfactory, we decided that we don't want any of our code to implicitly send 301 responses without cache-control headers. If there were use cases for caching 301 responses indefinetly long (which we couldn't think of), then we'd rather set this behaviour explicitly.

It turns out that Symfony once worked exactly the way we desire: Before #18220, Symfony would set cache-control to none unless configured explicitly. I see no strong arguments against this behaviour in neither the PR nor the corresponding issue.

So before we implement a custom solution I'd like to ask: would it be possible to revert the behaviour introduced in #18220, and have all Symfony users benefit from the safer behaviour?

Example 1

You have a long-running website called acme.com. The service at acme.com/product is available since decades, it has been promoted and printed on a lot of flyers both as text and QR code, people have bookmarked it and use it in their every-day work.

One day, a certain subset of pages at acme.com/other-product permanently moves its location to another company's website. Alice submits a patch to the Symfony code powering site that triggers 301 redirects under certain conditions. Sadly, under testing conditions, it wasn't appearent that this feature would accidentally add a 301 redirect to acme.com/product, too.

When you realise the problem after some hours, thousands of people have already been 301-redirected to the other site.

Because your Symfony app sent a 301 response without Cache-Control headers, those thousands of people will never again be able to use the acme.com/product URL to use your service – printed QR codes, social media and other online links and bookmarks will never work again for those people (unless they are tech-savvy and actively delete their browser cache).

You can't add a redirect back from the target URL, because the target URL is another very important URL which is owned by another business.

To not lose these users permanently, you have to switch to a new URL, and reprint all promotional material and re-promote it on social media.

Example 2

You decide to add a new service to your website, called "Product 2". You print a lot of promotional material using the URL acme.com/product2, and share it on social media. You tested the URL to work correctly on multiple devices.

But suddenly, you receive feedback from some customers, who can't access access acme.com/product2. After hours or days of debugging, Alice finds out that they get redirected to somewhere unrelated instead, but it's only on their devices and can't be repreduced anywhere else.

Turns out that 10 years ago, someone set up a 301 redirect from acme.com/product2. Those who used that redirect back in the day can't access the website you promoted for your new, important product.

Again, you have to redo the promotional campaign, with some image loss, as people might be confused by your back and forth and perceive you as unrealiable.

@nicolas-grekas
Copy link
Member

nicolas-grekas commented Jan 2, 2025

Shouldn't you use a 302 if you need a temporary redirect?
301 is meant for permanent redirects, which means cacheable by default indeed.
Adding the cache-control parameter you suggest here feel like hacking a 302 into a 301 to me.

@janopae
Copy link
Author

janopae commented Jan 2, 2025

Shouldn't you use a 302 if you need a temporary redirect?

Yes. But what if you need a permanent redirect, but still want to be able to recover in case of an error?

301 is meant for permanent redirects, which means cacheable by default indeed.
Adding the cache-control parameter you suggest here feel like hacking a 302 into a 301 to me.

Would you even use 302 redirects if the redirect is intended to be permanent, and you want search engines to update their results to the new location? So how would you get search engines to do so with a 302 redirect, which indicates the redirect was only temporary?

How would you make sure your code doesn't have bugs that make you acidentally respond with 301 responses?

I get that 301 is made for permanent redirects. But the default way browsers handle it seems to be made for fail-safe programmers.

@janopae janopae changed the title Don't let browser cache 301 responses forever by default Don't let browsers cache 301 responses forever by default Jan 6, 2025
@janopae
Copy link
Author

janopae commented Jan 7, 2025

Using a log to detect errors might be viable for usual errors that can be fixed.

The problem with errornous 301 responses is that they can't be fixed. As soon as they hit production, they can never be undone.

@janopae
Copy link
Author

janopae commented Jan 7, 2025

Thanks for the explanation, now I understand.

I know a few examples where a 301 response would be legitimate, because the redirect is considered permanent and we want services like search engines to update their links.

But in no case ever, we want to send a request that brings the client into a state where it will never recover from. I can't imagine anyone wanting that.

Should I list some examples where I think 301 is the correct status code (despite the undesirable default caching behaviour)?

@janopae
Copy link
Author

janopae commented Jan 7, 2025

As I said, I know there are legitimate 301 usecases (I even gave a reason why 301 is important sometimes), that's why I opened this issue. Because I don't really think this caching behaviour really has a lot of use cases, and I don't think this caching behaviour is something a lot of developers expect. I talked to developers with decades of experience in web development, and they were not aware of the irreversable implications a 301 has.

A 301 with Cache-Control headers is not the same as a 302, if that's where we misunderstand each other.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants