nginx rewrite preserving CGI query params (with a hash anchor) - nginx

For my www.example.com nginx config, I have these rewrite rules:
rewrite ^/foo$ https://one.example.com/page#one permanent;
rewrite ^/foo(\?.*)$ https://two.example.com/page$1#two permanent;
rewrite ^/bar$ https://three.example.com/page#one permanent;
rewrite ^/bar\?(.*)$ https://four.example.com/page?$1#two permanent;
A request for http://www.example.com/foo correctly redirects to https://one.example.com/page#one.
A request for http://www.example.com/bar correctly redirects to https://three.example.com/page#one.
A request for http://www.example.com/foo?extra=yes incorrectly redirects to https://one.example.com/page#one?extra=yes (I expect it to go to https://two.example.com/page?extra=yes#two).
A request for http://www.example.com/bar?extra=yes incorrectly redirects to https://three.example.com/page#one?extra=yes (I expect it to go to https://four.example.com/page?extra=yes#two).
How can I redirect to a page copying CGI parameters and linking to a particular anchor in the destination page?

It appears that the rewrite directive does not handle the # fragment correctly when assembling the query string into the replacement string.
You can prevent rewrite from appending the query string by adding a trailing ? to the replacement string. So, you can construct the correct result using the built-in variables $is_args and $args.
For example:
rewrite ^/foo$ https://one.example.com/page$is_args$args#one? permanent;
See this document for details.
Note that the query string is not part of the normalised URI used to match rewrite and location statements, so your ^/foo(\?.*)$ regular expression will not work.

Related

Rewrite Nginx URLs with Parameters (After a Question Mark)

I have URLS in this format:
/wiki/index.php?title=Widget
/wiki/index.php?title=Blue
/wiki/index.php?title=Red
/wiki/index.php?title=Etc
I want to be able to match any URL that has the pattern "/wiki/index.php?title=" or even just "/wiki/index.php" (but so it will pick up the above URLs with the "?") and then redirect them to simply /wiki/ (all pages that match the above pattern go to the single url /wiki/)
I used to have a Mediawiki install on the /wiki/ directory with a lot of pages in the format above. However now I am running a Wordpress install and it is using the /wiki/ directory. I don't need each rewritten URL to go to a different URL (I know that is difficult as my source URLs have parameters) but right now they all 404 and so I just want to direct to them /wiki/ at least.
Simple URL rewriting
The easiest case (rewrite all /wiki/index.php requests with any arguments) can be done by this config (inside your server block):
location = /wiki/index.php {
rewrite .* /wiki/?;
}
The '?' sign at the end of second rewrite parameter is a trick to completely remove any request arguments. Without it, request /wiki/index.php?title=Widget will be rewrited to /wiki/?title=Widget.
Rewriting only requests matching /wiki/index.php?title=... is a more complex, I don't know how to do it without if construction:
location = /wiki/index.php {
if ($request_uri ~ ^/wiki/index\.php\?title=) {
rewrite .* /wiki/?;
}
}
If you want to generate HTTP 301 or 302 redirect instead of simple URL rewriting, you can use redirect (for 301 temporary redirect) or permanent (for 302 permanent redirect) flag at the end of rewrite directive parameters (see documentation).
Rewriting URLs to individual pages
This task is not as difficult as it seems. For redirecting /wiki/index.php?title=Widget to /wiki/Widget, /wiki/index.php?title=Blue to /wiki/Blue etc. we can make use of map directive:
map $request_uri $uri_suffix {
~^/wiki/index\.php\?title=([^&]*) $1;
}
server {
...
location = /wiki/index.php {
rewrite .* /wiki/$uri_suffix?;
}
...
}
Please note that map directive must be declared outside your server configuration block!

Need to insert /#/ into a url

I am trying to create a try_files or rewrite rule. Only for a particular url with a query
http://example.com/survey?id=5 becomes http://example.com/survey/#/?id=5
This will do it:
location =/server {
rewrite ^ $scheme://example.com/server/# permanent;
}
Using $scheme preserves the scheme of the request (http or https).
Using location =/server matches requests to /server and you need the = to match only exact matches otherwise you'll keep getting redirected.

What's the rewrite syntax for an NGINX regex location?

Using NGINX as a load balancer running on 10.1.2.15:9002, I have a need to rewrite http://10.1.2.15:9002/proxy.stream?opt=1 to http://10.1.2.15:9002/app/proxy.stream?opt=1.
Following are bits from my nginx.conf file:
http {
upstream app_cluster {
server 10.1.2.23:8080;
server 10.1.2.25:8080;
}
server {
listen 9002 default_server;
location /app/ {
proxy_pass http://app_cluster/;
}
location ~ ^/proxy.stream(.*)$ {
rewrite ^(.*)$ /app/$request_uri last;
}
}
}
By the way, I can replace the rewrite line with return 401 (for example), and I can see the 401 HTTP status returned using Chrome Developer Tools, so I know the regex is matching. I just can't get the URI rewritten properly. In fact, I only see the original request with a 406 status in Developer Tools, so I suspect something is wrong with my rewrite syntax.
Does anyone see what is wrong with this configuration?
Using $request_uri in the replacement string of a rewrite statement is problematic, as it has not been normalised and also contains the query string, which by default, rewrite will append again.
Also, your replacement string contains //, as you are appending a URI which already has a leading /.
The regular expression location is not necessary, as a prefix or exact match location will suffice and is more efficient for nginx to process. See this document for more.
For example:
location /proxy.stream {
rewrite ^ /app$uri last;
}
Make use of the matching part from the regex instead of $request_uri
rewrite ^(.*)$ /app/$1 last;

Rewrite a url containing percent signs with nginx

My site needs to take what is essentially a malformed url sent by another site:
http://mysite/%SAMPLE_URL_STRING%
and redirect it to a proper url using nginx. None of the rewrite rules I've tried seem to catch it, instead always returning a 400.
As the single percent sign, not describing an encoded character, is itself an unsafe character as described in the RFC (http://www.ietf.org/rfc/rfc1738.txt) I suppose it might be an insurmountable issue.
However, sample (non-functional) rewrite rules include:
rewrite /%SAMPLE_URL_STRING% /redirect.html permanent;
rewrite ^/\%SAMPLE_URL_STRING\%$ /redirect.html permanent;
rewrite /\%.*\% /redirect.html permanent;
Even venturing into UTF8:
rewrite (*UTF8)^\x25.*\x25$ /redirect.html permanent;
Is it possible that any url containing %SAMPLE_URL_STRING% cannot be redirected by nginx?

Write a url path parameter to a query string parameter in nginx

I'm trying to rewrite a place name from a url path into a query string on nginx.
I want ourdomain.com/hotels/london?some_key=value to become ourdomain.com?d=london&some_key=value
We were doing it with Apache -
RewriteRule ^hotels/([^/]+)/?\??(.*)$ ?d=$1&$2 [QSA]
And we're currently doing on haProxy (acting as a reverse proxy) -
reqrep ^([^\ :]*)\ /hotels/([^/\ \?]+)/?\??([^\ ]*)(.*)$ \1\ /?d=\2&\3\4
How do I do the same thing on nginx?
Try something like:
rewrite ^/hotels/([^/]+)/?$ /?d=$1 permanent;
The nginx URI always has a leading /. The ? and query string is not part of the normalised URI, but the rewrite directive appends any arguments automatically unless there is a trailing ?.
See this document for details.

Resources