nginx multi-stage 404 handling - nginx

We just moved to a new site, and want to redirect old links where necessary - however, some still work. For instance,
/holidays/sku.html
still works, while
/holidays/christmas/
no longer works. I'd like to be able to allow the site to attempt to serve a page, and when a 404 is reached, THEN try to pass it through a series of regex redirects, that may look like:
location ~* /holidays/(.*)+$ { set $args ""; rewrite ^ /holidays.html?r=1 redirect; }
I'm using a ~* location directive instead of doing a direct rewrite because we're moving from a Windows-based ASPX site to Magento with php-fpm behind nginx, so we suddenly have to worry about case sensitivity.
Without using nested location directives (which are actively discouraged by nginx documentation) with an #handler of some sort, what's the best way to allow nginx to attempt to serve the page first, THEN pass it across redirects if it fails?
Thanks!

http://wiki.nginx.org/NginxHttpCoreModule#try_files

Related

NGINX: redirect request_uri that contains http

I have rewritten all external links sent to standard output on the fly as an internal link in PHP from https://externalsite.com to https://myexamplesite.com/https://externalsite.com, yet I want Nginx to handle the request for this URL and not PHP.
I have tried it via the line below, but it is not working
rewrite ^/http(.+)$ http$1 redirect;
Please advise on the best way to redirect https://myexamplesite.com/https://externalsite.com, to https://externalsite.com on request
I was able to have it work with this.
rewrite ^/https?\W+(.*) https://$1 redirect;
I am not sure why the former did not work but seems NGINX is passing it back internally.
NGINX seems not to honor an external redirect without the "https://" statically written before and jointly with the second option of the directive despite being captured in the parenthesis.

Nginx wordpress rewrites for pattern to a particular page

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.

Primary domain rewrite for nginx works fine, but doesn't direct single pages

We just moved our website to a new domain, and the new website uses a totally different URL structure for all of the pages on the website.
For example, the old URL might look like: http://example.com/product/product-name/
The new URL would look like this: http://www.example2.com/product-p/product-name/pro-n.htm
Here's a snippet showing an example of what I'm working with:
server{
location /product/product-name/ {
rewrite ^/product/product-name/
http://www.example2.com/product-p/product-name/pro.htm
permanent;
}
server_name example.com www.example.com;
rewrite ^ $scheme://www.example2.com;
}
As it's working now, http://www.example.com redirects to http://www.example2.com just fine.
However, if you try to visit http://www.example.com/product/product-name/ doesn't redirect to http://www.example2.com/product-p/product-name/pro.htm, it simply redirects to the root domain http://www.example2.com
Does anyone know how we redirect the primary domain AND redirect single pages to their new page?
Thanks,
You need to place your global rewrite directive into a location container:
location / {
rewrite ^ $scheme://www.example2.com;
}
#Richard's answer is fine.
I would only notice that permanent in the end of rewrite directive means 301 redirection, and such redirections are cached by all browsers, which can make it difficult to test. So while you are testing, I would recommend using redirect instead of permanent.
When you're done, put permanent back. Good luck!

Sending extra header in nginx rewrite

Right now, I am migrating the domain of my app from app.example.com to app.newexample.com using the following nginx config:
server {
server_name app.example.com;
location /app/ {
rewrite ^/app/(.*)$ http://app.newexample.com/$1;
}
}
I need to show-up a popup-banner to notify the user of the domain name migration.
And I want to this based upon the referrer or some-kind-of-other-header at app.newexample.com
But how can I attach an extra header on the above rewrite so that the javascript would detect that header and show the banner only when that header is present coz the user going directly at app.newexample.com should not see that popup-banner?
The thing is that, when you "rewrite" into URI having protocol and hostname (that is http://app.newexample.com/ in your case), Nginx issues fair HTTP redirect (I guess the code will be 301 aka "permanent redirect"). This leaves you only two mechanisms to transfer any information to the handler of new URL:
cookie
URL itself
Since you are redirecting users to the new domain, cookie is no-go. But even in the case of a common domain I would choose URL to transfer this kind of information, like
server_name app.example.com;
location /app/ {
rewrite ^/app/(.*)$ http://app.newexample.com/$1?from_old=yes;
}
This gives you the freedom to process at either Nginx or in a browser (using JavaScript). You may even do what you wanted intially, issuing a special HTTP header for JavaScript in new app server Nginx configuration:
server_name app.newexample.com;
location /app {
if ($arg_from_old) {
add_header X-From-Old-Site yes;
}
}
A similar problem was discussed here. You can try to use a third-party module HttpHeadersMore (I didn't try it myself). But even if it does not work at all, with the help of this module you can do absolutely everything. Example is here.
Your redirect is missing one thing, the redirect type/code, you should add permanent at the end of your rewrite line, I'm not sure what's the default redirect code if not explicitly mentioned.
rewrite ^/app/(.*)$ http://app.newexample.com/$1 permanent;
An even better way is using return
location /app {
return 301 $scheme://app.newexample.com$request_uri;
}
Adding a get parameter as mentioned above would also be a reliable way to do it, you can easily set a session ( flash ) and redirect again to the page it self but after removing the appended get parameter.
EDIT:
Redirecting doesn't send referrer header, if the old domain is still working you could put a simple php file that does the redirect with a header call.
header("Location: http://app.newexample.com")
One possible solution without any headers would be to check the document.referrer property:
if (document.referrer.indexOf("http://app.example.com") === 0) {
alert("We moved!");
}
Using a 301 will set the referrer to the old page. If the referrer doesn't start with the old page url, it was not directed by that page. Maybe a bit quick n dirty, but should work.

Remove www. from host name using Nginx Rewrite

I am trying to configure my Nginx to strip out www. from hostname.
I am looking for generic rule that handle possible subdomain cases.
E.g. If http://www.foo1.sample.com and http://www.fooX2.sample.com are two domains.
Then I want to strip any www. before such subdomains, and redirect them to http://foo1.sample.com and http://fooX2.sample.com respectively (WITHOUT knowing the exact subdomain in the rule.)
Thanks.
Best Practice:
The best practice, as well as the most efficient way, would be to use a separate server definition for this.
This will not only ensure that the config is automatically applied to every single one of the websites that are hosted on your nginx instance, but it'll also makes sure that you don't end up running regular expressions on the hostname from multiple instances in your code.
The following is the simplest form:
server {
listen 80;
server_name ~^www\.(?<domain>.+)$;
return 301 $scheme://$domain$request_uri;
}
If you do use https, then things get more complicated, because you'd have to ensure that the certificates don't mismatch. Unless you have a single certificate for all of your domains, it would be the best practice to simply hardcode everything, as it's already hardcoded in the certificate anyways, and a solution like above simply isn't possible due to the certificate requirements.
Alternative:
Note that the other answer to the question, which uses rewrite ^(.*) http://…$1 …, is incorrect, and will cause you to lose $query_string, as well as potentially mangle the encoding of the request as per Nginx pass_proxy subdirectory without url decoding.
If you require an if-based approach and no hardcoding, neither of which are recommended, e.g., like the other answer, then at least use the correct paradigm, to avoid the bloat and the loss of the $query_string; note that as per nginx server name regex when "Host" header has a trailing dot, the $host variable is already normalised by nginx (trailing dot removed, whole thing brought to lower case), so, you don't need to worry about doing a case-insensitive comparison like in the other answer, either:
if ($host ~ ^www\.(?<domain>.+)$) {
return 301 $scheme://$domain$request_uri;
}
References:
http://nginx.org/r/listen
http://nginx.org/r/server_name
http://nginx.org/r/return
http://nginx.org/en/docs/http/server_names.html
I think adding following If block in your Nginx conf file should work for you. It also takes care of subdomain case you mentioned.
if ($host ~* ^www\.(.*)) {
set $host_without_www $1;
rewrite ^(.*) http://$host_without_www$1 permanent;
}

Resources