I've got a strange case where my app is now for some reason appending a duplicate query string onto a URL that already has one. I don't have the access or the time to dig into the application code right now, but I do have access to Nginx configs.
What I need is a rewrite rule that will ignore the duplicate query string, ie
http://foo.com?test=bar?test=bar will redirect to http://foo.com?test=bar
Is this even possible? Thank you.
Here, add this inside your server block, before any other location or if:
if ($query_string ~ ^([^?]+)\?\1$) {
set $clean $1;
rewrite . $uri?$clean?;
}
It's quite tricky to get right, because there are more than a few obscure behaviours:
$query_string is the query string without the initial ?
the \1 in the regexp matches a second copy of the first group (...)
you need to save $1 into a new variable because rewrite will overwrite it
you need to use $uri and not $request_uri in the rewrite, because the latter contains a copy of the original query string
you need to add a trailing ? to the rewrite, otherwise the rewrite would append a copy of the original query string! (that's probably the most obscure bit of Nginx syntax, ever)
The simple form of rewrite (without the redirect flag) should be enough for most applications, plus it doesn't require an additional round-trip to the browser.
If it does not work for you, you can try using the $clean query string into the appropriate backend directive (such as fastcgi_param QUERY_STRING and such) or, as a last resort, issuing a browser redirect using the redirect rewrite flag.
Related
For a site running Mediawiki, I have this config for clean URLs:
rewrite ^/wiki/([^?]*)(?:\?(.*))? /wiki/index.php?title=$1&$2 last;
This works fine except when the page title needs to have a question mark. For a URL like /wiki/Who_is_your_daddy%3F_It_is_me the correct rewritten URL is /wiki/index.php?title=Who_is_your_daddy%3F_It_is_me. However, Nginx is rewriting to /wiki/index.php?title=Who_is_your_daddy&_It_is_me.
What is the correct rewrite rule?
The query string is not part of the normalised URI that is processed by the rewrite directive's regular expression. However, the query string will be automatically appended to the rewritten URI, so you don't need to do anything to include the &action=edit part. See this document for details.
The %3F in the original request will be normalised to a ? by the time that rewrite is processing it. You will need to capture both sides of the ? to manually translate it back to %3F in the new query string.
You can use more than one rewrite statement, so that the case with and without a %3F are both handled correctly.
For example:
rewrite ^/wiki/(.*)\?(.*)$ /wiki/index.php?title=$1%3f$2 last;
rewrite ^/wiki/(.*)$ /wiki/index.php?title=$1 last;
Quick question. We have two apps. Ports 3001 and 3002. Our domain is www.domain.com.
What we want to have it once person enters www.domain.com/pathname we want them to be redirected into another app's specific path.
How to do it?
We already came up to this in my nginx
location /pathname/ {
proxy_pass http://127.0.0.1:3002/;
}
It nearly works. However, our app under 3002 works on path /#/pathname.
We can access it by typing www.domain.com/pathname/#/pathname. We want to access same link by typing www.domain.com/pathname.
How to shorten it? What do I miss?
(upd) Just redirect /pathname to /pathname/#/pathname
According to your comment, you want just redirect from /pathname to /pathname/#/pathname
Try these combined directives:
rewrite to append # and fragment identifier
and proxy_pass to reverse proxy to the app.
E.g.:
location /short_path_name/ {
rewrite ^ /pathname/#/$uri permanent;
break;
}
location /pathname/ {
proxy_pass http://127.0.0.1:3002/;
}
And use www.domain.com/short_path_name/ link for your app.
Unfortunately, nginx can't see the fragment identifier
Unfortunately, you can't. Because server never get the fragment identifier from browser.
The fragment identifier functions differently to the rest of the URI: its processing is exclusively client-sided with no participation from the web server
Naming a bit amusing, but it has a long history. See TBL (1997): Fragment Identifiers on URIs:
The URI reference is a thing you build by taking a URI for an information object, adding a "#" sign and then a Fragement identifier. (The last term is historical, so try not to thinl of it necessarily identifying a fragment).
Workarounds
There are workarounds, e.g. encode hashtag symbol into %23 but I'm not sure is it your way.
Handle request arguments with nginx
Note: rewriting url, nginx can preserve request arguments if you add ? at the end of rewrite directive.
See Nginx rewrite manual:
If a replacement string includes the new request arguments, the previous request arguments are appended after them. If this is undesired, putting a question mark at the end of a replacement string avoids having them appended, for example:
rewrite ^/users/(.*)$ /show?user=$1? last;
I'm trying to set it up so that certain URLs will rewrite (not change URL in address bar) to a particular page with identifying parameters.
I would like
site.com/reviews/froot_loops_review.php AND
site.com/reviews/froot_loops_review
to actually show
site.com/review?title=froot_loops
Additionally, I would like to be able to append any other parameters so:
site.com/reviews/froot_loops_review.php?var=1
would show what's at:
site.com/review?title=froot_loops&var=1
I'm having a hard time with this because it's my first time editing nginx conf files this is as far as I've got but it doesn't work (I think because it doesn't make the first match):
location /reviews/ {
rewrite ^/reviews/(.*)_review.php(.*) /review?title=$1 break;
return 403;
}
What am I doing wrong here? What should my next step be? What is a good resource for learning Nginx rewrites?
I am not sure if this is strictly an answer. But there are multiple issues with your current solution.
This is a WordPress site, so there is already a location ~ \.php$ block which is handling any URI that ends with .php. The URI /reviews/froot_loops_review.php will never make it to the location /reviews/ block in your question.
You can use the ^~ modifier on the location directive to force nginx to ignore any matching regular expression location block. See this document for details.
The rewritten URI cannot be processed within the same location block, so break is the wrong suffix to use. You will have more success using last. See this document for details.
But the main problem is WordPress itself. WordPress normally uses the REQUEST_URI (which is the URI as originally presented) to identify the requested page. Which is why we can rewrite everything to /index.php and still get the right page.
Trying to make WordPress use your rewritten URI without an external redirect (which you say you do not want to do) is tricky. You may be better off looking for a WordPress plugin to achieve it - or try to make the pretty permalinks work the way you want.
I have a file, say client.app.js, which I want to send when http://mydomain.local/app.js is requested.
I want to only send a single specific file when get a specific request.
How do I configure nginx to work like this?
To change the URI you can use a rewrite directive. The simplest solution is a rewrite statement at the server block level, or within the location block that processes the original request (assuming that /client.app.js is a valid URI) (see this document for details):
rewrite ^/app.js$ /client.app.js last;
Alternatively, use an exact match location. This also gives you the ability to set the root of the replacement file (see this document for details):
location = /app.js {
root /path/to/file/dir;
rewrite ^ /client.app.js break;
}
I am trying to integrate a WordPress plugin (Jetpack's Related Posts module) which adds query strings to the end of post URLs. I would like to cache URLs with FastCGI while completely ignoring the query strings/$args.
My current config is: fastcgi_cache_key "$scheme$request_method$host$request_uri";
I am aware of using the solution mentioned here to turn my $skip_cache variable off for URLs containing a certain $arg, which works. However, I want to cache the same result regardless of the value of the $args rather than using a unique cache key for each set of $args.
I am also aware of some suggestions to just use $uri in the fastcgi_cache_key rather than $request_uri; however, because $uri is not just the original requested URI minus the $args, something in the WordPress architecture (likely pretty links) forces all requested URIs to return the same cache result (rather than a different result for each page).
Is there any way to truly use the originally requested URI without including the $args in the cache key?
Just now, I had similar problem.
So, my solution:
In the nginx config add to the
http {
...
map $request_uri $request_path {
~(?<captured_path>[^?]*) $captured_path;
}
...
}
Then you will have a variable $request_path, which contains $request_uri without query_string.
So use $request_path for cache key
fastcgi_cache_key "$scheme$request_method$host$request_path"
Important. The "map" directive can be added only to "http {}". This directive will be execute for all requests for all hosts.