Bug in non-trailing slash rewrite

I have successfully implemented the instructions in this guide about deploying multiple repos to a single, custom domain-enabled, site.

However, I am experiencing a bug that contradicts the docs on a trailing slash in redirect rules.

normal condition: When a url is entered into a browser that contains the trailing slash, the rewrite is successful and the sub directory is rendered as expected.

irregular condition: When a url is entered into a browser that does not contain the trailing slash, the rewrite happens, but THERE IS NO CSS APPLIED, so the sub directory is not rendered as expected.

Here is my _redirects file, the screenshots above are related to the 5th redirect rule, but the irregularity happens for rules 3 and 4 as well:

# zelip.me/talks

/talks https://zelip-talks.netlify.com/talks 200!
/talks/ https://zelip-talks.netlify.com/talks 200!
/talks/using-github https://zelip-talks.netlify.com/talks/using-github/ 200!
/talks/node-mvc https://zelip-talks.netlify.com/talks/node-mvc/ 200!
/talks/azellaz-jamstack https://zelip-talks.netlify.com/talks/azellaz-jamstack/ 200!
/talks/* https://zelip-talks.netlify.com/talks/:splat 200!
1 Like

Hi, @brianzelip. I believe this issue is happening because of two reasons:

  • the CSS files are referenced as a relative path ( meaning href=“css/reveal.css”)
  • the redirect rule is a rewrite rule and not a redirect rule (because of the 200 status code in the rule)

For example, say the the page is browsed using this:

This makes the current path /talks/. The relative path of css/reveal.css is appended to this making the full path /talks/css/reveal.css. However, this file doesn’t exist at this path and results in a 404 (resource not found).

Now take the following request URL as the example:

This makes the current path /talks/azellaz-jamstack/ and full path for the relative file (css/reveal.css) into /talks/azellaz-jamstack/css/reveal.css. This file does exist and it works as expected.

So, how does one resolve this issue? There are two solutions for this.

The first and recommended solution is make that redirect rule a “rewrite” rule be changing the HTTP status code used from 200 to 301 like so:

/talks/azellaz-jamstack https://zelip-talks.netlify.com/talks/azellaz-jamstack/ 301!

This will redirect the browser (and change the URL in the address bar) to the new URL. This version will end with a / which will make the relative path for those files correct.

The other possible solution (but not the recommended one) is to make the file references using absolute paths (starting with a / like /talks/azellaz-jamstack/css/reveal.css). Again, this is not required if you use the first solution above.

To clarify, if the redirect is made into a 301 status rule (instead of the 200), no code changes are required.

Would you be willing to please test this and let us know if it resolves the issue or not?

2 Likes

Thanks @luke!

Can you clarify something for me, I think you may have inadvertently made a typo. See the bold here:

So, how does one resolve this issue? There are two solutions for this.

The first and recommended solution is make that redirect rule a “rewrite” rule be changing the HTTP status code used from 200 to 301 like so:

I think those are supposed to be swapped right? So it should read:

The first and recommended solution is make that rewrite rule a “redirect” rule be changing the HTTP status code used from 200 to 301 like so:

1 Like

Happy to clarify! :slight_smile:

We call all our rules “redirects” and we call this subclass of redirect (meaning a redirect using a 301 response) a “rewrite”. In other words “a rewrite redirect” or “a redirect of subclass rewrite”).

This is because this rule will rewrite the URL in the address bar. We have other redirects rules which do not rewrite the URL for example.

The reference for this is our Redirects documentation. On that page the “rewrite” subclass of redirect is in the section Rewrites and Proxying.

I think the complication here comes from other ways these two words (rewrites, redirects) are frequently used:

  • people to often refer to a 301 HTTP status code response as a “redirect”
  • the Apache HTTP server uses rules called “rewrites”

We do use the words a bit differently. I hope this explains more clearly and, if not, I’m happy to answer other questions.

(If you decide to test it, I’m also curious if switching to the 301 rule resolves the issue - especially if it does not.) :smiley:

2 Likes

Hi @luke. I’ll follow up here on 2 matters:

  1. the sake of clarity about redirect and rewrite
  2. the outcome of your advice

on clarity between redirect and rewrite

This is just a nitpick about a typo really.

Here’s what the Rewrites and Proxying docs say:

When you assign an HTTP status code of 200 to a redirect rule, it becomes a rewrite .

Here’s what you wrote in your first comment above:

The first and recommended solution is make that redirect rule a “rewrite” rule be changing the HTTP status code used from 200 to 301 like so:

My point in bringing this up was just to point out the typo in your useful comment - 200 is the status code I was using before your advise. Per the docs, 200 is the actual “rewrite”, where 301 is the redirect. You suggested I change from 200 to 301, ie: from “rewrite” to “redirect”.

So your sentence above should instead be (see bold for what should change):

“The first and recommended solution is make that rewrite rule a redirect rule be [sic] changing the HTTP status code used from 200 to 301 like so:”

the outcome of your advise

It works!

BUT, the change of url in the address bar is exactly what I’m trying to avoid! I want it to look to the user like all content is coming from the same base url.

This means that Netlify cannot be used as a replacement for what GitHub Pages provides in this matter. Using GH Pages, I had the base custom domain (zelip.me) set from my brianzelip.github.io repo, and any other repo I set to be published via Pages, was available at zelip.me/*. In that GH Pages case, no matter if there was a trailing slash or not, EVERYTHING WORKED as expected.

In the current state of Netlify, leaving out the trailing slash, while using a rewrite via custom domain, DOES NOT WORK.

2 Likes

Hi @brianzelip, sorry about that typo. A 301 is a redirect, while a 200 can be a rewrite. If the domain you are proxying is a netlify site then we’ll do a rewrite. If you 200 and proxy an external site, then we are still doing a proxy request (like a reverse proxy) for that request since we can only rewrite for paths we serve.

Can you link to your deploy that was experiencing the originally reported issue so I can take a look at it, please?

2 Likes

Hey @futuregerald - there’s certainly no sweat about the typo! Was just trying to clarify for the sake of my own understanding, and those coming after.

Y’all leave so many trails to the past (which is dope!) I’m not sure which link you’re looking for, so I’ll add a few:

  1. deploy details just before changing from 200 to 301

  2. deploy preview just before changing from 200 to 301

  3. code just before changing from 200 to 301

  4. see the issue in action in this, non-trailing slash, example

1 Like

Hey @brianzelip, circling back to this - we chatted with our Docs team about this, and we have an issue filed to iron our any inconsistencies, just FYI :slight_smile:

1 Like

Hey, I’m experiencing the same thing as OP @brianzelip

If one tries to redirect domain.com/path -> domain.com/path/, one gets a redirect loop, so this isn’t possible :frowning:

My current workaround is a terrible client-side JS hack:

    if (!window.location.pathname.endsWith("/")) {
      window.history.pushState(null, null, `${window.location.href}/`);
    }

What happened with this post in the end? It seems to have fizzled out, but I can’t find a solution within the replies

hmm, thanks @mrbear. I’m checking in with our docs team about any instructions we are providing, i will allow others to weigh in about potential workarounds.

@perry cool

Just to add an observation, if it was possible to 301 domain.tld/path -> domain.tld/path/ without causing any loop, and then to 200 rewrite domain.tld/path/ to someotherdomain.tld/someotherpath/, there wouldn’t be any issue.

1 Like

Hi @mrbear, can you link me to a real site that’s having this issue so we can take a look, please? Thanks.

Hey @futuregerald

If you compare hitting these two:

https://no-workaround.mrbear.info/the-list

https://no-workaround.mrbear.info/the-list/

Source codes are at

The rewrite rule doesn’t work when you have an existing redirect for that route. Also, since you are familiar with your code, I’m curious why it doesn’t load properly without the /. Your JS bundle and CSS are still loading.

@futuregerald my understanding is that without the / at the end, the JS bundle and CSS loaded are those of the origin and not of the rewritten host. So the wrong JS bundle and CSS are being loaded without the /.

Where do I have a rewrite rule that already has an existing redirect that negates it?

@futuregerald @perry

any word on this?

Hi @mrbear, can you change your redirect to the following and let me know if it works better?:

/the-list* https://thelist.mrbear.info/:splat 200!
/* /index.html 200

Hello you all,

I’m having a very similar issue, so I might maybe give you more points of data.

/society/ works correctly, but /society (note the missing trailing slash) does not:


As you can see, it fails to load all assets. Those assets are referenced with a relative path (./style.css) so I would expect them to load relative to the ./society/ folder. Unfortunately, it seems that Netlify treats my ./society path as a file, and thus tries to load the assets from the root, and not the ./society/ folder.

I tried the above suggestion (/society* https://gamemaster-society.netlify.com/:splat 200!) but got the same result.

Happy to provide further information if needed

(Note: I’m getting errors from Discourse telling me I cannot post more than 6 links as I’m a new user, which makes it pretty cumbersome to explain my case here :wink: )

1 Like

So it looks like your links for your css and js files are not relative. When going to Megadungeon helper, the links try to load as image. I think if you change your link and script tags to show href="style.4f8d993218.css" instead of href="./style.4f8d993218.css" (minus the dot and forward slash), the redirect will work in both with and without trailing slashes. Let me know if it does.

Hey everyone,

I was also wondering about this, since adding a trailing slash to folders is a pretty standard web server behavior. (Not sure if it’s an actual standard, but it’s definitely expected behavior in nginx and Apache to do a 301 redirect from /directory to /directory/).

…and after signing up for Netlify Analytics today I ended up at this thread from Google thinking I was doing something wrong, since even it gets confused by this non-standard behavior:

My suggestion would be to just fall in line with what nginx and Apache (and apparently GitHub Pages) do to make things simpler, but this is just my two cents. :slight_smile:

Thanks as always!