queryStringParameters not working with redirect on production

I have redirect rule and netlify dev understand that as query string parameter but production version not.

[[redirects]]
from = "/feed/:series/"
to = "/.netlify/functions/feed-builder/?series=:series"  
status = 200

You can check the source code: https://github.com/raneio/supla-rss
(check netlify.toml and functions/feed-builder.js)

ok, our documentation about query string params is only going to apply to INCOMING params - in the “from” in your example. However, I don’t see any reason why that shouldn’t work on the surface.

Could you tell us what the entire URL you see in your function (when you log it), is? Trying to understand if we just don’t fill in the value properly, or if instead we drop the ?series= part too.

Also might try it without the / at the end of the path and see if that works better:

to = "/.netlify/functions/feed-builder?series=:series"

events in dev and production


Reson why i don’t just use normal query params like ?series=123 is cache. Cache doesn’t respect anything after ? character. So it’s give same content fo ?series=123 and ?series=456.

Hi @rane, I noticed that the path’s value in the event object has the series number. While this doesn’t explain why the query params is not getting passed on, you could try using the path directly instead. Let me know if that workaround works.

I am experiencing the same issue, on netlify dev the rewrites work correctly, but in production the parameters are not passed. I do not have any prod-specific settings.

https://5f761c54ccce8e00074c4731--compassionate-khorana-4a579b.netlify.app/echo/foobar shows {}
http://localhost:8888/echo/foobar shows foobar

was this ever resolved?

rewrite looks like /echo/:msg /.netlify/functions/echo?msg=:msg 200, I can confirm that it is working in general (if I change the path name to not match the function it rewrites correctly), just the query parameters aren’t set

Hey @parkan,
I think you’ll be interested in this :slight_smile: Changes to Redirects with query string parameters are coming

@jen unfortunately the other post covers 301/302 redirects and how they handle query parameters (bringing their behavior in line with the 200 redirect that’s being used here)

the issue here is different: the rewrite rules in local dev environment allow captured path parameters to be rewritten arbitrarily into the target URL, whereas in production this does not happen (i.e. moving the :msg parameter from path to query param does not happen)

I personally really like the dev environment behavior, as it saves a lot of unnecessary path re-parsing in my function code (the original path does get passed in the event AFAIK, so it is possible to add a middleware to re-parse it, but that seems silly given that the rewrite already has the necessary information), so I’d love to see it enabled in production

however, if that is not possible it should disabled on dev so the behavior is consistent – it’s possible that it automagically works because of some express functionality that netlify dev (but not netlify’s production env) uses

I think you hit the nail on the head with your conclusion. The cli is more an emulation of how Netlify works in production. I do feel that it would be easier to change the behavior on the cli vs the production code at this time. Would you mind filing an issue about that here: https://github.com/netlify/cli/issues?

Opened a bug here: https://github.com/netlify/cli/issues/1605

FWIW I still think the production behavior is a huge limitation, as functions must be aware of their paths to be able to parse their parameters, which is brittle, verbose, and not DRY. I am almost tempted to stand up an nginx proxy to handle these rewrites for me (to keep the path parsing logic out of the function code), but that would nullify the advantages of netlify’s infrastructure.

I think the ideal behavior would be for {path} parameters in redirects to automatically populate the pathParameters property on APIGatewayEvent, as they do on AWS API Gateway.

2 Likes

thanks for opening an issue on that repo, @parkan. appreciate it!

I’m troubleshooting something very similar, but I think even more basic in that I’m not passing a variable in from to to and I still have the same issue. Everything works on local with netlify dev but when I push to a preview branch it doesn’t work.

tldr:

Given this rule in my _redirects file:

/api/v1/search /.netlify/functions/api-v1-search?siteId=macos 200

With netlify dev on my local machine, event.queryStringParameters.siteId in my function is macos.

When pushed to a preview branch, event.queryStringParameters.siteId in my function is undefined.

More Context

I want to include a specific query parameter for a given domain, so the value gets hardcoded into the redirect itself at build time. I have a siteId variable that gets set in the build. So my _redirects template file looks like this:

/api/v1/search /.netlify/functions/api-v1-search?siteId=${siteId} 200

When this gets built, the resulting _redirects file on disk puts out a redirect rule like this:

/api/v1/search /.netlify/functions/api-v1-search?siteId=macos 200

The weird thing is, like others in this thread, the way that rule functions seems to differ between what i’m running locally with netlify dev and what is running in a preview branch build even though the redirect rule is the exact same.

Local redirect

When I run things locally using netlify dev, I get a _redirects file on disk with this rule:

/api/v1/search /.netlify/functions/api-v1-search?siteId=macos 200

Great. My function actually echos back the URL that was hit, so when i hit /api/v1/search on local using netlify dev, it gives me back this in the response:

"links": {
  "self": "https://www.macosicongallery.com/api/v1/search"
},

Cool. Everything works as expected locally. So I push to a preview branch.

Preview Branch redirect

So now i have the same code that was working locally, but now being built remotely by netlify’s servers and on a preview branch. Through some logging in the build, I’ve confirmed that the rule in the _redirects file is the exact same one in the local build:

/api/v1/search /.netlify/functions/api-v1-search?siteId=macos 200

But when i hit /api/v1/search on this remote preview URL – https://5fd447f2a818530007016641--macosicongallery.netlify.app/api/v1/search – it doesn’t echo back the same thing when I did it locally. I get undefined in place of where i was getting macos on my local build:

"links": {
  "self": "https://www.undefinedicongallery.com/api/v1/search"
},

To me, this means that the request coming into the lambda function isn’t detecting a siteId query paramenter. But i’m unsure how/where I’m doing the wrong thing, especially since it’s working locally using netlify dev (although, it sounds like from this thread, netlify dev is just an approximation, which is obviously terribly misleading in this case).

Given what Netlify’s docs say:

Query string parameters are not automatically passed to our redirect engine.

I’m still not sure why this rule isn’t working:

/api/v1/search /.netlify/functions/api-v1-search?siteId=macos 200

I’m not trying to passing query params from /api... to /.netlify/functions.... I’m just tacking on a hard-coded query param in the to, which I’m expecting to be passed to the invoked lambda. But somehow it’s being set to undefined on my preview branch.

What a great post, you clearly have the “Support Engineer mentality” in specifying your experiments and results very clearly!

The docs are a bit confusing there since we’re halfway through this work: Changes to Redirects with query string parameters are coming but TL;DR the “200” redirect is more of a “rewrite” and there query string params ARE intended to be passed on as is without a specific param1=:value1 as documented here: https://docs.netlify.com/routing/redirects/redirect-options/#query-parameters.

I think you’re saying that they are not, in fact, passed on - and I think that may be due to a bug around how we cache - we don’t always cache separately based on param, so you may have gotten a result from a prior param-less request when you made yours maybe? But, I am not so sure of that diagnosis. Could you let me know if your function is still deployed somewhere I can access it, to do some testing to see if I can figure things out? (I get a 404 at the presumed prod URL https://www.macosicongallery.com/api/v1/search)

1 Like

the query string params ARE intended to be passed on as is without a specific param1=:value1 as documented.

I think you’re saying that they are not, in fact, passed on

That’s precisely right. They do not appear to be getting passed on. Not only that, but my rule isn’t even trying to capture anything to pass along! It’s just saying redirect one path to another path and include a hard-coded parameter i.e. /path /otherpath?q=value

I have not yet deployed to production—precisely because of this bug—but have a feature branch build of the feature. You can access it here.

Of note:

I added a log to my build process which outputs the contents of my _redirects file. Looking at the build for the above linked feature branch, I can see the output of my _redirects file includes the proper rule:

Notice the rule?

/api/v1/search /.netlify/functions/api-v1-search.js?siteId=macos 200

As I would expect given what’s documented about the redirects/rewrites engine, that should be passing on any query params. But if you do a GET to the following URL:

https://5fd447f2a818530007016641--macosicongallery.netlify.app/api/v1/search

The lambda will return an error because it’s expecting a siteId parameter but isn’t getting one.

If you include siteId explicitly in a GET request, i.e.:

https://5fd447f2a818530007016641--macosicongallery.netlify.app/api/v1/search?siteId=macos

It works as expected. But, as noted, I’m not expecting to have to pass that parameter explicitly given my rewrite rule, which doesn’t appear to be functioning as expected.

Hi @jimniels,

Yea, I think that was due to the fact that the code that was enabling the pass-through was reverted but has since been re-fixed. Can you confirm that this is the case?

For some context, the post mentions the fix was re-deployed 8 days ago.

@Dennis nope, still not working for me.

Just ran a rebuild of my site above. My build outputs my redirect, which looks like this:

3:35:39 PM:   '/api/v1/search /.netlify/functions/api-v1-search?siteId=macos 200', 

You can see my rule is saying to redirect to another path with a hard-coded query param (i.e. /path to /.netlify-fn?foo=bar) but the query param is not showing up in the lambda.

You can test this at the preview build. Do a GET to:

https://5febaf1576d34a0007ef33aa--macosicongallery.netlify.app/api/v1/search?name=swift

It doesn’t work. You’ll see the siteId parameter is coming back from the lambda function as undefined. If you hard code it in the GET, it works:

https://5febaf1576d34a0007ef33aa--macosicongallery.netlify.app/api/v1/search?name=swift&siteId=macos

So the redirect is still not working. Or maybe I’m missing something?

I feel like I must be missing something really basic here?

All I’m doing is a redirect from a path to a new path with a (hard-coded) query string param.

/path/to/resource /redirect/path?with=query 200

How does that not work?

Following up with more context. Hoping @fool or @Dennis can help me out.

So I’ve been playing with this a bit more to try and figure it out. I’ve found that doing a regular redirect rule works as expected. But using rewrites doesn’t work as I would expect.

Note: all of these examples are using my preview build at: https://5ff7956eba84190008348a88--macosicongallery.netlify.app

Redirects (301s)

Here’s the redirect rule in my _redirects file:

/netlify-support-test-no-200 /404.html?foo=bar

Because I didn’t specify a HTTP code, it uses a 301 and redirects the user from one path to another with the specified query params.

If I curl that resource, I can see I get a 301 redirect to the 404 page with the query parameters.

If I hit that URL in my browser – /netlify-support-test-no-200 – it redirects to the 404 page with the query parameters in the URL bar.

Screen Shot 2021-01-07 at 4.19.04 PM

Cool, that works just as expected.

Rewrites and proxies (200s)

If I setup a URL to rewrite with a 200, then I don’t see the query parameters show up. Here’s the rule in my _redirects file:

/netlify-support-test /404.html?foo=bar 200

If I curl this resource, it returns a 200 with the contents of the 404 page. That makes sense:

If I hit it directly in my browser (/netlify-support-test) I see the contents of the 404 page (same as the redirect above) but my browser’s URL doesn’t change (and doesn’t have the query params in it)

Screen Shot 2021-01-07 at 4.24.22 PM

That all makes sense. Because this is a rewrite, Netlify is making a request to the URL with the query params behind the scenes, so any query params are hidden from the URL bar and from the curl.

But that request that’s happening behind the scenes isn’t working as expected

I have one more rewrite rule setup. This one is designed to be a rewrite that points to a URL with query params. Here’s the rule:

/netlify-support-test-params /.netlify/functions/netlify-support-test?foo=bar 200

Note that this is pointing at a netlify (lambda) function with explicit query parameters. The lambda function’s code is really simple. It’s essentially echoing back the query parameters.

Screen Shot 2021-01-07 at 4.45.14 PM

So my expectation is that if I curl /netlify-support-test-params I’ll get a 200 and it will echo back {"query": {"foo": "bar"}. But it does not. The lambda receives no query parameters:

However, if I do include some query params myself in the request, it echoes those back. For example, if I curl /netlify-support-test-params?hello=world, I get a 200 with {"query":{"hello":"world"}} echoed back to me:

What gives?

Is my expectation incorrect here? It appears Netlify’s rewrites/proxy engine is not actually making a request with query params in tow. To restate:

Given this rule: /path /rewrite?with=params 200

I expect: a request to /path will result in Netlify making a request to /rewrite?with=params behind the scenes and then returning a 200 result to /path.

But what I get is that a request to /path results in, apparently, Netlify making a request to /rewrite without any query params and returning a 200 result to /path.

1 Like

Hey there,
We put this question to the team who works on our proxy and they explained that rewrites to functions work differently than rewrites to other paths. They suggested that you could accomplish what you want by grabbing the incoming path (/.netlify/functions/netlify-support-test?foo=bar) directly in your function using event.path and then parsing the param you’re interested in within your function. Sorry for the confusion here. Let us know if that solution would work for you.

rewrites to functions work differently than rewrites to other paths

Well that’s interesting. That must be the source of my problem.

They suggested that you could accomplish what you want by grabbing the incoming path…directly in your function using event.path and then parsing the param you’re interested in within your function.

That doesn’t work. Or maybe I’m misunderstanding the suggestion?

Let’s say I have the following redirect:

/thing /.netlify/functions/thing?foo=bar 200

If I do a GET to /thing that results in the invoked function’s event.path being /thing, which as I type that I guess makes sense in the context of function calls. Given that case, however, there are no query parameters present and it appears impossible for a function call to know about its /.netlify/functions/path along with any query params that might be present, unless you do a GET to it directly.

So—and I’m writing out loud here—it sounds like my expectation is completely wrong. Given the redirect above, I would expect a GET to /thing to proxy a call to the netlify function URL, which would result in the function having even.path be /.netlify/functions/thing?foo=bar, which I would then be able to parse. But that’s not how it’s working. The location that is being proxied to—the /.netlify/... url—is completely hidden from the invoked function.

Thus, from what I can tell in the implementation, it seems impossible to add query string parameters to a redirect for a function call, i.e. even given a hard-coded redirect of

/path /.netlify/functions/path?foo=bar 200

It’s impossible for the invoked function to know anything about foo=bar.

Hi!

I suggested the workaround of using the request path in the function, and I’ll try to explain it a bit better.

Context

Yes, the event.path in a function will always be the path of the original incoming request.
This is mostly legacy behaviour that was implemented like this once and we can’t really change it now without breaking people’s existing functions.

Primary use case: This means that you can use redirects to make Netlify invoke a function for any path. Great, right?

Guessing your use case

I’m guessing what you want to do is rewrite to a function from different paths and pass some data along in the different redirects based on which rule was matched, so that the function can behave differently using that added context.
Yes, this is where the redirects mechanism falls a bit short.

Solution 1: Do the matching inside your function

Since our redirect mechanism is not able to provide you the data of which rule it matched, you could try to match the original request in your function to determine what it should do.

Example:

_redirects

/first-thing /.netlify/functions/the-only-function 200!
/second-thing /.netlify/functions/the-only-function 200!

the-only-function.js

exports.handler = async function(event, context) {
    if (event.path === "/first-thing") {
        // do one thing
    } else if (event.path === "/second-thing") {
        // do the other thing
    } else {
        // return 400 status, because the function was invoked raw
    }
}

Solution 2: Proxy headers hack

When you rewrite something you can add custom proxy headers (click for docs).

The same example solved using those:

netlify.toml

[[redirects]]
from = "/first-thing"
to = "/.netlify/functions/the-only-function"
status = 200
force = true
headers = {X-Variant = "first-thing"}

[[redirects]]
from = "/second-thing"
to = "/.netlify/functions/the-only-function"
status = 200
force = true
headers = {X-Variant = "second-thing"}

the-only-function.js

exports.handler = async function(event, context) {
    if (event.headers["X-Variant"] === "first-thing") {
        // do one thing
    } else if (event.headers["X-Variant"] === "second-thing") {
        // do the other thing
    } else {
        // return 400 status, because the function was invoked raw
    }
}

Hope this helps you solve your specific issue!

1 Like